Skip to content

Building a guide

Guides are interactive, step-based learning content built from a ZIP package of Markdown and assets. PlusPlus renders the package into a step-by-step codelab asynchronously. The upload mirrors native videos: request a URL, PUT the ZIP, poll until ready.

Flow at a glance

sequenceDiagram
    participant Your app
    participant PlusPlus API
    participant Storage
    Your app->>PlusPlus API: POST /guides/uploads/ { name }
    PlusPlus API-->>Your app: 201 { upload_id, upload_url, guide_id }
    Your app->>Storage: PUT (application/zip)
    loop until ready or errored
        Your app->>PlusPlus API: GET /guides/uploads/{upload_id}/status/
        PlusPlus API-->>Your app: 200 { status, launch_url }
    end

1. Request an upload URL

Two modes:

  • Create-and-upload — pass name. PlusPlus creates a hidden guide and binds an upload URL to it. It auto-unhides once processing succeeds.
  • Attach to existing — pass guide_id. The new package replaces the active version; the guide's existing is_hidden is preserved.
curl -X POST https://acme.plusplus.app/api/v2/guides/uploads/ \
  -H "Authorization: Bearer pp_your_token_here" \
  -H "Content-Type: application/json" \
  -d '{ "name": "Setting up your dev environment" }'
{
  "upload_id": "upl_guide_abc",
  "upload_url": "https://uploads.plusplus.app/guides/…",
  "guide_id": "01HXYDBKW5V2H6F3M8R9N0Q2YP",
  "expires_at": "2026-04-29T13:00:00Z",
  "status": "awaiting_upload"
}

2. Upload the package

The presigned URL is scoped to application/zip — send exactly that content type:

curl -X PUT "$UPLOAD_URL" \
  -H "Content-Type: application/zip" \
  --data-binary @./dev-env-setup.zip

3. Poll until ready

curl https://acme.plusplus.app/api/v2/guides/uploads/upl_guide_abc/status/ \
  -H "Authorization: Bearer pp_your_token_here"
{
  "upload_id": "upl_guide_abc",
  "status": "ready",
  "launch_url": "https://acme.plusplus.app/guides/dev-env-setup/"
}
status Meaning
awaiting_upload URL issued; storage hasn't received the ZIP.
processing Uploaded; PlusPlus is extracting and rendering (seconds to a minute).
ready Done. launch_url is populated.
errored Processing failed. Check error_message and upload a new package to retry.

There's no separate confirm call — a single polling loop drives the whole flow. Poll every 3–5 seconds.

Package shape

A guide package is a ZIP whose root contains exactly one Markdown file (.md, not named README.md), optionally with image assets and a custom style.css. PlusPlus uses the claat tool to render it into a step-based codelab. The most common errored causes are a missing Markdown file or an invalid ZIP structure — surface error_message to your users.

Re-uploading

To replace a guide's contents, request a fresh upload URL with guide_id mode and upload again. Learner progress is preserved across re-uploads unless the section structure changes substantially.

Common pitfalls

  • Wrong content type on the PUT. The presigned URL is bound to application/zip. Sending anything else fails the upload.
  • Multiple Markdown files in the ZIP root. Exactly one is required.
  • Expecting auto-publish on re-upload. Create-and-upload guides auto-unhide; guide_id re-uploads preserve whatever visibility you'd set.