A complete SKILL.md
--- frontmatter block carrying name and description, then free-form Markdown. Everything after the closing --- is stored as written and never parsed — write it for the agent that will read it, in whatever structure makes the instructions clear.
The file has to be named SKILL.md, has to decode as UTF-8 (SKILL.md must contain valid UTF-8 text.) and has to be 256 KB or smaller. One file is the whole skill; there is no way to upload supporting files alongside it. The rest of the upload flow — where the button is, what the picker accepts, how versions appear in the table — is on Skills.
The frontmatter contract
The parser is deliberately small. It is not a general-purpose YAML parser, and it sits on an authorization boundary, so it accepts a narrow shape: anything it does not recognise is ignored, and the two fields it requires are enforced.- The first line must be
---, and the next line that is just---closes the block. - Inside the block only
key: valuelines are read. Keys are lowercased, and unknown keys are ignored rather than rejected — so an extra key carried over from another tool does no harm, but a misspelleddiscriptionleaves the requireddescriptionunset and the upload fails on that instead. - Surrounding single or double quotes are stripped from a value.
- A value of
>opens a folded block and|opens a literal block; the indented lines that follow are joined with spaces or with newlines respectively, and the first blank line ends the block — anything after it is dropped. nameanddescriptionare both required and must be non-empty after trimming.nameis limited to 120 characters,descriptionto 2,000.
The name becomes the slug
The slug is derived fromname: accents are stripped, everything is lowercased, runs of anything that is not a letter or a digit become a single -, leading and trailing dashes are trimmed, and the result is cut to 64 characters. Quarterly Revenue Report and quarterly-revenue-report both produce quarterly-revenue-report.
The slug, not the display name, is the identity, and the skill’s identifier is derived from your organization together with that slug. Two admins who upload the same name converge on one skill rather than forking into two, and the slug is what decides whether your next upload is a new skill or another version of an existing one.
That has one consequence worth planning for: you can change a skill’s wording, its description and even its displayed name across versions, but not its slug. A name that slugs differently is a different skill. Pick the slug you want before the first upload.
Re-uploading is how you edit. Receipt compares content, not file names: identical content is a no-op that adds no version and reports nothing, and changed content becomes the next version, which is what new runs receive. See Skills for the version, enable, disable and delete lifecycle.
Writing one an agent will open
Every enabled skill is mounted read-only into each new run and listed in a compact catalog carrying only each skill’s name, description, version, content hash and path; nothing ranks those entries or matches them to the request, and the agent decides which files to open. Skills covers that mounting, and the reading discipline Receipt asks the agent to follow, in full. Four consequences for how you write the file: The description is the routing surface. With the name, it is the only part of the catalog entry that says what your skill is for. Write it as a trigger — when this skill applies, and to what — rather than as a summary.Use when asked to produce or refresh the quarterly revenue report… earns an open; Notes about reporting does not.
Keep one skill to one job. A short file that gets opened is worth more than a manual that gets skipped, and that discipline explicitly discourages loading everything.
Make it self-contained. The catalog carries metadata only, and your upload is a single file, so anything the agent needs has to be in the body you wrote.
Do not ask a skill to change its own surroundings. The mounted catalog is read-only, and a run rejects any patch that touches those paths, so an instruction to edit or rewrite a skill in place cannot succeed.
A skill is instructions, not permission. Task scope, the user’s authorization and Receipt’s execution constraints take precedence over anything a skill says, so a skill cannot widen what a run may do — write allowlists still decide that, per connection. See tools and permissions.
If you generate the file from chat
In the web app you can ask Beetle — the assistant’s name in the interface — to write the file for you, review the draft it returns, and then — as an owner or admin — ask for it to be saved. Using skills in chat covers that conversation end to end. Two rules matter here, because they constrain the file rather than the flow:1
The generated name must already be a slug
A file you write by hand may use any display name and gets a slug derived from it. A generated one may not: its
name has to be in lowercase-hyphen form already, or the save is refused with Generated SKILL.md name must use lowercase-hyphen form.2
The generated frontmatter must contain exactly name and description
Unknown keys are tolerated in an uploaded file and rejected in a generated one:
Generated SKILL.md frontmatter must contain exactly name and description. A draft over 262144 bytes — the same 256 KB ceiling — is rejected before you see it.--- boundaries, the 120- and 2,000-character limits, the slug rules — applies identically to both paths, because both go through the same parser.
Next step: see how much of all this your organization actually uses.