---
title: "Create or replace a saved package by writing a complete UTF-8 text file set (no binary assets)."
method: POST
path: "/v1/packages"
tags: ["packages"]
---

# Create or replace a saved package by writing a complete UTF-8 text file set (no binary assets).

`POST /v1/packages`

## Request

- Base URL: `https://api.kody.codes`
- URL: `https://api.kody.codes/v1/packages`
- Auth: HTTP bearer

Create or replace a saved package by writing a complete UTF-8 text file set (no binary assets). Coding agents with local filesystem/git access should prefer packageGetGitRemote (pass create: true for new packages) to clone, edit, push, and publish with packagePublishExternalPush; tool-only agents use packageSave or repo sessions. The package repo is rooted at package.json and package.json#kody is the Kody-specific metadata block. Publish requires non-empty root README.md (human-focused: what it does, prerequisites, setup, done-when, plus a concise Intent section) and AGENTS.md (agent-focused: imports, smoke tests, edge cases). Ask the user if intent is unclear. Visibility is a repo setting (`packageUpdate` / `repoUpdate`), not package.json `"private"`. Leftover `"private"` in manifests is ignored for catalog listing. New packages are created private; set `changes.visibility: "public"` after the user asks to list them on /community. Production package source safety policy: never replace source history, force publish, or packageSave over an existing package unless Kody has created a restorable backup snapshot and the user explicitly approved destructive overwrite. If existing source cannot be cloned or verified, stop and report the source recovery problem.

Org permission: `package:write`.

## Request body

- object
  - `package_id` string: Optional saved package id to update in place. Omit to create a new saved package.
  - `files` object[], required: Full package file set to write. Must include package.json at the repo root.
    - `path` string, required: Package-relative file path to write into the saved package repo.
    - `content` string, required: Full file contents for this package file. Publish requires non-empty root README.md (human-focused, including a concise Intent section) and AGENTS.md (agent-focused: imports, smoke tests, edge cases).
  - `confirm_destructive_overwrite` boolean: Set to true only when the user explicitly approved destructive overwrite of existing package source history. Promoting publishes replace advertised history with a new root commit (and drop leftover session refs); promotePublished:false stays additive. Restorable backups retain prior content — secret scrub of backups still needs packageDelete or an explicit purge.
  - `confirm_private_visibility_change` boolean: Set to true only when the user explicitly approved changing package.json `"private"`. This flag does not change repo visibility: catalog listing is `packageUpdate` `changes.visibility`. Leftover `"private"` in manifests is ignored for the catalog.

## Response `200`

Success.

- object
  - `package_id` string, required
  - `kody_id` string, required: Package name leaf (same value as `slug`). Kept as a compatibility alias; prefer `slug` or `name`.
  - `slug` string, required: Package name leaf (the path segment after the scope). Same value as `kody_id`.
  - `name` string, required
  - `description` string, required
  - `tags` string[], required
  - `has_app` boolean, required
  - `hidden` boolean, required
  - `visibility` 'public' | 'private', required: Repo visibility / forkable. Public means default-branch HEAD is world-readable and forkable and the package appears on /community. Private is owner-only. Independent of internet_visible.
  - `internet_visible` boolean, required: When true, the hosted package app may be served on the public internet after capability approval. Independent of visibility/forkable. Defaults false.
  - `locked_at` string, nullable, required: When set, publishes require a website click at /@{username}/{kodyId}/approve-publish?commit=<full 40-character SHA>. Agents may lock via packageUpdate (`changes.locked: true`). Unlocking is website-only at /@{username}/{kodyId}/settings. Do not abbreviate the commit.
  - `source_id` string, required
  - `created_at` string, required
  - `updated_at` string, required
  - `next_steps` string, required: Follow-up guidance for continuing package work, including the git clone-edit-push lane for coding agents and any pending package secret approvals.
  - `pending_secret_package_approvals` object, nullable, required: Pending user-secret package approvals detected from secretMounts and secret placeholders. Null when none are pending. Do not treat the package as ready to run until the user approves these and a static-import smoke test from execute succeeds against a read-only export or dry-run input that actually uses the approved secret.
    - `package_id` string, required: Saved package id that needs approvals.
    - `kody_id` string, required: Package name leaf that needs approvals (same value as `slug`).
    - `slug` string, required: Package name leaf that needs approvals.
    - `secrets` object[], required: User secrets that still need package approval.
      - `secret_name` string, required
      - `approval_url` string, required: Per-secret package approval URL in the account UI.
    - `bulk_approval_url` string, nullable, required: One-click bulk approval URL when two or more secrets still need package approval; prefer this over individual links.

## Other responses

- `400`: Error envelope `{ "error": { "code", "message", "details"? } }`.
- `401`: Error envelope `{ "error": { "code", "message", "details"? } }`.
- `403`: Error envelope `{ "error": { "code", "message", "details"? } }`.
- `404`: Error envelope `{ "error": { "code", "message", "details"? } }`.
- `409`: Error envelope `{ "error": { "code", "message", "details"? } }`.
- `413`: Error envelope `{ "error": { "code", "message", "details"? } }`.
- `429`: Too many requests (edge IP/token limit) or an entitlement / plan-cap limit. Wait at least `Retry-After` seconds before retrying.
- `500`: Error envelope `{ "error": { "code", "message", "details"? } }`.
- `503`: Error envelope `{ "error": { "code", "message", "details"? } }`.
- `default`: Error envelope `{ "error": { "code", "message", "details"? } }`.

---

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