---
name: create-vmax-presentation
url: https://vmax.ai/skill/slides
description: Create, revise, share, export, and publish a Vmax presentation on a user's behalf with their API key. Covers the Markdown dialect vmax.ai/slides compiles (slides, front matter, layouts, directives, images, live Campaign Application embeds, shader backdrops), the writing conventions every Vmax deck follows, and the exact API handoff that turns an agent's Markdown into a deck the user saves once on vmax.ai. Use whenever an agent is asked to make, draft, rewrite, present, share, download, or publish slides for a Vmax account.
---

# create-vmax-presentation

A Vmax [presentation](https://vmax.ai/glossary#presentation) is a slide deck
saved on the user's account through the slides API at
`https://api.internet.dev` and edited at `https://vmax.ai/slides`. Its source is
Markdown. The vmax.ai editor compiles that Markdown into positioned slides:
it picks each slide's layout, sizes every line of type to fit the canvas, adds
the Vmax watermark, and renders live Campaign Applications and shader
backdrops. You write the Markdown; the user's editor does the layout.

This file is served at `https://vmax.ai/skill/slides`. Terms link to
`https://vmax.ai/glossary`.

## What you need

- **The user's API key**, sent as `X-API-KEY` on every call. It is the same key
  the [posting guide](https://vmax.ai/skill) uses. Keep it out of deck content,
  URLs, file names, and logs.
- **A verified account.** Personal presentations need account level 10 or
  above and no organization, subscription, or credit account. Never send
  `domain`: every Vmax presentation is personal.
- **What the deck is for.** If the request does not say, ask once for the
  topic or source material, the audience, and roughly how many slides. Ask
  before sharing or publishing; never do either unprompted.

Check the key first:

```bash
curl -s -X PUT https://api.internet.dev/api/users/viewer \
  -H 'Content-Type: application/json' -H "X-API-KEY: $VMAX_KEY"
```

A `viewer` with a `level` of 10 or more can create presentations.

## How a deck is made: the handoff

The Vmax layout is computed by the vmax.ai editor, not by the API. The API's
own `markdown` input is a different, plainer importer: it rejects front matter,
starts slides only at `---`, and accepts only `asset:` images, so the dialect
below must never be sent there. Instead, hand the Markdown to vmax.ai:

1. **Write the deck** in the dialect below, as one Markdown string under
   120 KiB (UTF-8).
2. **Upload every picture first** (see Images) and put each returned
   `fileURL` in the Markdown.
3. **Create the deck with an empty document and the Markdown in `data`:**
   `POST /api/slides/create` with a fresh `idempotency_key`,
   `document: { "title", "description", "slides": [] }`, and
   `data: { "vmax_slides": { "markdown": "<the whole Markdown>" } }`. Use the
   front matter `title` (or the first heading) as `document.title`.
4. **Give the user the editor link**, `https://vmax.ai/slides?deck={id}`, and
   tell them to press **Save** once. A deck whose stored document has no slides
   but whose `vmax_slides.markdown` does opens as unsaved; that one Save
   compiles the slides, uploads the posters for embeds and backdrops, adds the
   watermark, and stores the finished document. Until then the editor preview
   and its Present view already show the real layout, and anything that reads
   the stored document (a presentation link, an export, a publication) has
   nothing to show.
5. **Continue only when asked.** Once `GET /api/slides/{id}` returns a
   document with slides, you can share, export, or publish it (below).

```bash
jq -n --rawfile markdown deck.md \
  --arg key "$(uuidgen)" --arg title 'Find the hard tasks. Train on them. Find harder tasks.' \
  '{ idempotency_key: $key,
     document: { title: $title, slides: [] },
     data: { vmax_slides: { markdown: $markdown } } }' |
curl -s -X POST https://api.internet.dev/api/slides/create \
  -H 'Content-Type: application/json' -H "X-API-KEY: $VMAX_KEY" --data-binary @-
```

The answer is `{ data, success: true }`. Keep `data.id` and `data.version`. A
new deck answers 201; replaying the same `idempotency_key` returns the same
deck with 200. Reuse a key only to retry the identical request; any changed
input gets a new key.

The same call in JavaScript:

```js
const response = await fetch('https://api.internet.dev/api/slides/create', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json', 'X-API-KEY': key },
  body: JSON.stringify({
    idempotency_key: crypto.randomUUID(),
    document: { title, slides: [] },
    data: { vmax_slides: { markdown } },
  }),
});
const { data: deck } = await response.json();
const editorLink = `https://vmax.ai/slides?deck=${deck.id}`;
```

## Write the Markdown

### Slides

- A level one (`#`) or level two (`##`) heading starts a slide, unless the
  current slide is still empty. The heading is the slide's headline.
- A line of three dashes (`---`) starts a slide with no heading. Writing `---`
  between headed slides is harmless and keeps the source easy to scan.
- Level three to six headings stay inside the slide (see Columns).
- A deck holds at most 100 slides.

### Front matter

An optional block at the very top:

```markdown
---
title: Find the hard tasks. Train on them. Find harder tasks.
description: Why Campaign exists, in seven slides.
label: Vmax Campaign
canvas: 1280
---
```

- `title` names the presentation; without it, the first heading does.
- `description` is private metadata.
- `label` is printed as written, small, on every slide.
- `canvas` is `1280` (16:9, the default) or `960` (4:3). Height is 720.
- `theme` accepts only `light`. Presentations are always light; leave it out.

Any other key is ignored with a warning.

### Blocks and the layout they choose

Never set a size or a position. Each slide's layout is chosen from what it
holds, in this order:

| The slide holds | Layout |
|---|---|
| An image or a Campaign Application embed | `image`: headline and caption on the left, the picture on the right |
| A fenced code block | `code`: headline above a code panel |
| A table | `table` |
| Two or three `###` sections, each with text under it | `columns` |
| A blockquote and no heading | `quote`: set large and centred, any paragraph under it as attribution |
| Only a heading | `title` on the first slide, `section` after it |
| The first slide's heading and one short line (140 characters or fewer) | `title` |
| A heading and paragraphs | `statement`: headline left, prose right |
| A heading and a list | `bullets` |
| Anything else | `stack`: headline above the body |

Paragraphs, bulleted and numbered lists (nested items indent; `- [ ]` task
items keep a box), blockquotes, fenced code, and GFM tables all work. Inline
**bold**, *italic*, `code`, and `http`, `https`, or `mailto` links work; any
other link scheme renders as plain text. Body text is set in a muted grey and
**bold is lifted to full strength**, so bold is how a slide says which words
matter. Headlines keep their text exactly as written: nothing is changed to
capitals.

### Directives

HTML comments are instructions for the slide they sit in. A comment written
before a heading belongs to the previous slide, so put directives under the
heading.

| Directive | Effect |
|---|---|
| `<!-- notes: … -->` | Speaker notes, shown in the presenter's notes drawer, never in exports or public views. A paragraph starting with `Notes:` does the same. |
| `<!-- hidden -->` | Keeps the slide in the editor and out of every export, presentation link, and publication. |
| `<!-- layout: name -->` | Forces `title`, `section`, `statement`, `bullets`, `image`, `quote`, `code`, `table`, `columns`, or `stack`. A layout that cannot show the slide's content warns and falls back to automatic. |
| `<!-- background: #rrggbb -->` | This slide's background colour. |
| `<!-- label: … -->` | This slide's label in place of the front matter one. |

Any other comment becomes speaker notes. Other HTML is printed as text.

### Images

A line holding only `![alt](source)` is an image. A slide shows at most two.

- **Upload pictures yourself, then use the returned URL.** Request an upload
  with `POST https://api.internet.dev/api/data/generate-presigned-url`, body
  `{ "type": "image/png", "file": "chart.png", "size": <bytes>, "domain": "vmax.ai" }`,
  PUT the bytes to the returned `uploadURL`, and write the returned `fileURL`
  (`https://intdev-global.s3.us-west-2.amazonaws.com/public/…`) as the source.
  PNG, JPEG, or WebP, at most 15 MB. That URL is stored as the image as is.
- Any other `https://` URL or `/` site path is fetched by the user's browser
  when they save and uploaded then. A host that refuses the browser's request
  fails the save and names the picture, so upload first whenever you can.
- The alt text is the picture's caption placeholder, its exported alt text,
  and what a screen reader says. Describe the figure fully: what is plotted,
  against what, and what it shows.

### Campaign Application embeds

A slide can hold a live, interactive Campaign Application drawn on Vmax's
checked-in example campaigns. Write it as an image whose source is `campaign:`
and an application name:

```markdown
![The best-case frontier run](campaign:conclusion-timeseries-v2)
```

| Name | Application |
|---|---|
| `conclusion-timeseries-v2` | Campaign Conclusion Timeseries v2 |
| `conclusion-timeseries` | Campaign Conclusion Timeseries |
| `v2` | Campaign Rollout Explorer v2 |
| `v1` | Campaign Rollout Explorer v1 |
| `files` | Campaign Rollout File Explorer |

A heading, one paragraph, and one embed make the shape of a product feature
section. The editor, the presenter, presentation links, and publications draw
the application live; downloads show a still poster the user's Save uploads.
Do not add a title to the line: Save writes the poster's URL there itself.

### Shader backdrops

One line anywhere in a slide puts it on a live shader:

```markdown
# Your headline

![](backdrop:voronoi-ribbons)
```

`voronoi-ribbons` is the only backdrop. The slide turns dark with white text
while every other slide stays light; downloads show a poster frame. Use it for
a title or section slide, not for dense content.

### Fitting and limits

Type steps down until each block fits its box. When a slide cannot fit even at
the smallest size, the editor names it in a warning: split that slide. Never
shorten words into abbreviations or drop content to squeeze it in.

- 100 slides, 100 elements per slide, 20,000 characters in any one text block.
- Tables up to 50 rows and 20 columns; long ones read better split.
- A list too long to sit beside its headline moves under it, and eight or
  more bullets there split into two columns.
- The whole Markdown must stay under 120 KiB so it fits in `vmax_slides`.

## Conventions every Vmax deck follows

These are how the example presentations on `vmax.ai/slides` read. Follow them
unless the user asks otherwise.

- **One idea per slide.** The headline is the idea, a short sentence in
  sentence case; the body supports it in one to three sentences or three to
  six bullets. If a slide needs more, it is two slides.
- **Headlines say the outcome.** "Watch the frontier fill in." beats
  "Frontier visualization". A title deck may use a noun phrase ("PROPEL"), but
  never all capitals.
- **Open with a title slide:** the heading and one short line. Close with what
  the audience should do next.
- **Bold the few words that carry the slide**, never a whole paragraph.
- **Put what you would say in notes,** not on the slide.
- **Every picture is a real figure** with a full alt description, uploaded to
  the deck. Never invent charts or numbers.
- **Claims are grounded.** Use only numbers, names, and results the user gave
  you or that their sources state. No testimonials, customer names, or metrics
  without a source.
- **No middle-dot separators** (`A · B`): put the parts on separate lines or
  bullets. **No em dashes**: use a comma, a colon, or a second sentence.
- **Write Vmax** (capital V, lowercase max) in prose; `VMAX.ai` and `vmax.ai`
  keep their own casing.
- **Presentations are light.** Do not write `theme: dark`; a backdrop slide is
  the only dark slide.
- **Campaign vocabulary follows the glossary.** Campaign finds, measures, and
  marks frontier tasks; it never keeps, filters, or targets them. A model
  learns in training, not by attempting a task. The loop is
  [recursive self-improvement](https://vmax.ai/glossary#recursive-self-improvement):
  find the hard tasks, train on them, find harder tasks. Campaign does the
  finding; training runs in the user's own pipeline.

## A complete deck

The Campaign presentation, in the shape of the `vmax.ai/use-campaign` page:

```markdown
---
title: Find the hard tasks. Train on them. Find harder tasks.
label: Vmax Campaign
---

# Find the hard tasks. Train on them. Find harder tasks.

Recursive self-improvement is one loop, repeated. Campaign does the finding, every round.

---

# Every round needs new tasks.

- Find the hard tasks: the ones your model solves only some of the time
- Train on them, until your model passes them every time
- Find harder tasks at its new edge, and go again

<!-- notes: Mixed results are the learning signal. A task the model always passes teaches it nothing, so every round of training needs a fresh frontier. -->

---

# Watch the frontier fill in.

Every measured task that lands in the 12% to 50% band joins the frontier while the run is still going.

![The best-case frontier run](campaign:conclusion-timeseries-v2)

---

# Trace any result to the evidence.

Open any attempt and follow it step by step, from the task it was given to how it was graded.

![The completed example campaign](campaign:v2)

---

# What the frontier is worth

### An asset that compounds

Recursive self-improvement compounds only as fast as new tasks arrive at a model's edge.

### Training data with signal

Run reinforcement learning on tasks your model solves some of the time.

### Built for long runs

Each run leaves a certified task pack that starts the next, so the loop keeps turning.

---

# Start the loop with one page of instructions.

Campaign is open to approved Vmax accounts. Join the waitlist at vmax.ai/use-campaign.
```

## Revise, share, export, and publish

Every write carries the deck's current `version` as `expected_version` and a
fresh `idempotency_key`. A `409` means someone else saved first: read the deck
again, reapply your one intended change, and send it with the new version.
Never overwrite a newer deck with a stale copy.

- **Revise the Markdown.** `GET /api/slides/{id}`, then
  `POST /api/slides/update` with `id`, `expected_version`, an
  `idempotency_key`, `document: { "title", "slides": [] }`, and
  `data: { "vmax_slides": { …the current vmax_slides, with the new markdown and without layouts… } }`.
  `data` merges one top-level key at a time, so send the WHOLE `vmax_slides`
  object: sending `{ "markdown" }` alone erases the deck's watermark and
  publication record. The empty document makes the editor open the revision as
  unsaved; ask the user to Save again. Until they do, the presentation link and
  new exports show nothing, while a publication keeps its pinned revision. If
  `vmax_slides.markdown` is missing (a very large deck, or one written by
  another tool), do not rebuild it: ask the user to edit on vmax.ai.
- **Share with a presentation key** (only when asked). Generate 18 random
  bytes as unpadded base64url (24 characters of letters, digits, `-`, and
  `_`), then `POST /api/slides/update` with `data: { "presentation_key": KEY }`
  and nothing else. The link is
  `https://vmax.ai/slides?deck={id}&view=present#key={KEY}`: full screen, no
  sign-in, always the latest save. The key belongs after `#` so it never
  reaches a server log; never put it in a query string. `presentation_key: null`
  stops the link.
- **Download.** `POST /api/slides/exports/create` with `deck_id`,
  `revision_id` (the deck's `current_revision_id`), `format` (`html`, `pdf`, or
  `png`), and an `idempotency_key`. Poll `GET /api/slides/jobs/{job}?deck_id={id}`
  after `poll_after_ms` until `succeeded`, `failed`, `cancelled`, or `expired`.
  `artifacts` carry download URLs that last five minutes; poll the job again
  for fresh ones within seven days.
- **Publish** (only when asked). Export `html` for the current revision and
  wait for success, then `POST /api/slides/publish` with `deck_id`,
  `revision_id`, `expected_version`, and an `idempotency_key`. The public page
  is `https://vmax.ai/slides/shared/{data.id}`. Record it for the editor by
  updating `vmax_slides.publication` to `{ "id", "revision_id" }` inside the
  whole `vmax_slides` object. A publication pins that revision; later saves do
  not change it until the owner publishes again. `POST /api/slides/unpublish`
  revokes it.

## Common mistakes

| Mistake | What happens | Instead |
|---|---|---|
| Sending this Markdown as the API's `markdown` field | `400` on front matter, or a plain deck split only at `---` | Empty `document`, Markdown in `data.vmax_slides.markdown` |
| Sending part of `vmax_slides` | The watermark and publication record are erased | Read, modify, and send the whole object |
| Linking pictures from other hosts | The user's Save can fail on a refused fetch | Upload first and use the `fileURL` |
| Sizing, positioning, or colouring text by hand | Nothing: the dialect has no syntax for it | Let the layout fit it; split long slides |
| `theme: dark` | Read as light | Use a backdrop slide for a dark moment |
| Cramming a slide | A fitting warning names the slide | Split it into two |
| Sharing, publishing, or exporting unprompted | Content leaves the account | Wait for the user to ask |
| Reusing an `idempotency_key` with new input | `409` | A new key for every new request |

## For contributors

The compiler, the editor, and the site's client live in this repository.
Their contracts are [`use-slides-api`](../use-slides-api/SKILL.md) and
`next-vmax-tools/AGENTS.md` under `## /slides`. Update this skill whenever the
dialect, the handoff, or a limit changes.
