Personal playbook authoring guide
Official Kody guide
A playbook is private executable code owned by the signed-in user. Its complete
text files and metadata live in the application database. package.json is the
manifest. There is no Git checkout, community catalog, fork, publish step, or
retained history.
Create
Call playbookSave with a complete map of UTF-8 text files. Include
package.json and every referenced source or types file. Binary files are not
supported. Save validates the manifest, source limits, and execution artifacts
before accepting the source. Failure leaves the previous saved source unchanged.
Saving does not execute external actions.
import { kody } from 'kody:runtime'
export default async function main() {
return await kody.playbookSave({
files: {
'package.json': JSON.stringify({
name: '@your-username/hello',
exports: { '.': './main.ts' },
kody: {
description: 'Return a personal greeting.',
tags: ['example'],
},
}),
'main.ts':
'export default function main(input: { name: string }) { return { greeting: `Hello ${input.name}` } }',
},
})
}Use the signed-in user's actual username in the manifest. Save returns
playbook_id and edit_token. Keep the stable ID for later reads and edits.
README.md and AGENTS.md are optional documentation, never save gates.
Edit safely
- Call
playbookGet({ playbook_id })for the current completefiles, export contracts, andedit_token. - Modify that file map. Preserve every unchanged file you intend to keep.
- Call
playbookSave({ playbook_id, expected_edit_token: current.edit_token, files: completeFiles }). - Keep the new
edit_tokenfrom the successful save.
The file map is a complete replacement, not a patch. Omitted files are
deleted. An existing ID always requires expected_edit_token; do not create a
new playbook as a workaround for a stale-token conflict. On conflict, get the
current files again, reconcile the user's intended changes, and save with the
new token. Never blindly retry an old replacement against a fresh token.
An edit token is concurrency control, not a version or history entry. No restore or rollback history exists. To undo an edit, read current files, change them back deliberately, then save with the current token.
Manifest and exports
name: scoped name@your-username/playbook; its leaf is the URL slug.exports: callable modules, such as{ ".": "./main.ts" }.kody.description: concise description, at most 200 characters.kody.tagsand optionalkody.searchText: personal search metadata.- Optional runtime surfaces:
kody.jobs,kody.app,kody.subscriptions,kody.retrievers, andkody.webhooks. - Declare Worker-compatible npm modules under
dependencies. - Declare direct static playbook imports under
kody.dependencies, using{ "@your-username/helpers": "*" }. Dependencies remain owner-scoped and resolve current saved code; there are no publication pins.
Use JSDoc on exported functions (or their declared types file) to explain purpose, inputs, outputs, and an import-and-call example. Search uses it to help choose the right export. Optional README intent and agent notes may help maintenance but are not required.
Run and verify
After a successful save, new runs use current saved code. An already-started run
finishes with what it loaded. Inspect playbookGet for the exact call shape,
then invoke an export through execute:
import hello from 'kody:@your-username/hello'
export default function main() {
return hello({ name: 'Ada' })
}Use read-only smoke tests or a playbook-defined dryRun that genuinely skips
external writes. Synthetic invocations have real side effects:
- Exports: import and call with representative inputs; test expected errors.
- Apps:
playbookAppFetchchecks status, headers, body, and storage effects. Browser verification remains necessary for layout and OAuth redirects. - Subscriptions:
playbookSubscriptionDispatchwith a safe fixture. - Jobs: begin disabled, test the no-argument scheduled wrapper, then enable.
- Webhooks: validate authentication, replay protection, and payload handling.
Obtain fresh user confirmation before irreversible external actions. A save, successful check, or dry run is not consent to send, charge, or write.
Permissions and storage
Use existing secret and integration APIs rather than hardcoding credentials. Secret access, host approval, integration allowlists, and runtime isolation still apply. Editing never widens permissions automatically. Resolve required approvals before testing an authenticated call.
Use playbookStorage() for durable personal playbook data. Jobs, exports, apps,
and subscriptions share the playbook's storage identity. Do not put runtime
state or secrets into source files to bypass these APIs.
Apps and larger workloads
Build app links from playbookContext.hostedUrl and
playbookContext.appBasePath; do not hardcode a username or mount prefix. See
Playbook apps.
If a dependency cannot fit Worker CPU or memory limits, keep a thin orchestrator and use heavy-work offload. Do not skip validation or attempt to store binary source in the text-file map.
Delete
Use playbookDelete only when the owner requests deletion, with playbook_id
and the exact confirm_name returned by inspection. Deletion is permanent and
cleans up owned runtime surfaces, grants, storage, and derived search/cache
data. It is not hiding, and no retained source history can restore it. See
Personal playbook lifecycle.