Skip to main content

View source on GitHub

The smallest useful recipe for starting an OpenHands Cloud conversation that has a plugin pre-loaded, using only the V1 App Server REST API. One field — plugins — does the work. This example is self-contained: it ships its own plugin in dad-joke/ and loads it straight from this repo on GitHub. No external marketplace, no API keys beyond your OpenHands key.
A plugin is a small git-hosted bundle of slash commands, skills, hooks, and/or MCP servers (Claude Code “plugin marketplace” format). dad-joke ships one of each kind we need: a /dad-joke:about slash command that tells a dad joke about an animal, and a keyword-triggered skill that asks for your favorite animal first.
Want a clickable link / README badge instead of code? See the companion example launch-plugin-badge, which builds on this one.

How It Works

POST /api/v1/app-conversations is asynchronous. It returns a start task, not a finished conversation. The script polls GET /api/v1/app-conversations/start-tasks?ids=<task_id> until the task yields an app_conversation_id, then prints the conversation URL. (Omitting sandbox_id from the request lets the server provision a fresh sandbox. To attach to a sandbox you prepared yourself, pass its id — see clone-and-attach.)

The One Field That Matters

A plugin spec has three parts:
The plugin is fetched from the ref you name. While iterating on a branch, pass --ref your-branch so the fetch finds your copy; it resolves to main once merged.

Two Ways to Drive It

The initial_message decides what happens once the plugin is loaded:
  1. Run a skill immediately via an entry command. Send the plugin’s slash command as the first message. The SDK registers /dad-joke:about as a keyword trigger, so this tells a joke right away:
  2. Load the plugin, then prompt normally. Send a natural-language message; the plugin’s skills are available for the agent to use when relevant. The bundled skill fires on “dad joke”, asks for your favorite animal, then delivers:

Run It

Options

Loading a Private Plugin

The bundled plugin is public, but source also accepts a full Git URL, and a ${VAR} placeholder in the source (or ref) is expanded against the conversation’s secrets just before the repo is cloned — so you can fetch a private plugin without hard-coding a token.
Version requirement. Secret expansion in the plugin source landed in software-agent-sdk#3758 and ships in the agent-server runtime v1.29.0 (released 2026-06-18). A conversation only gets the fix when its sandbox runs agent-server:1.29.0-python or newer.In OpenHands Enterprise (tracked by the Replicated openhands chart version), 0.7.65 is the first release to bundle that image — the VM-based release being cut for it. Not yet on the Stable channel or OpenHands Cloud (app.all-hands.dev) as of this writing.On older builds the ${VAR} reaches git clone literally and a private source fails at conversation start; a public source is unaffected.
There are four ways to supply the credential — all reference it by name in the URL, so the raw token never has to appear in the source: This script demonstrates scenario 3 with --secret (which adds a secrets field to the request):
Scenario 4 needs no --secret at all: if you’ve connected GitHub/GitLab/ Bitbucket to OpenHands, just reference e.g. ${GITHUB_TOKEN} and the managed token is injected for you. Good to know:
  • Braced ${VAR} only — a literal $ in a token is never mangled.
  • Secrets only, not host env — host environment variables are never folded into the URL (that would be a credential-exfiltration vector).
  • Missing secret → left untouched — the placeholder stays verbatim, so you get a clear clone failure rather than a surprising default.
  • Stays redacted — the persisted plugin spec keeps the ${VAR} placeholder, not the secret value.
  • HTTPS, not ssh:// — the credential travels inside the URL; SSH authenticates out-of-band (a key), so there is no placeholder to expand.

The Bundled Plugin

launch-plugin-badge

turn this into a no-code launch link, HTML button, or README badge.

Plugin Marketplace

the plugin directory: a browseable catalog of plugins (served at /plugins) loaded from a marketplace source repo.

Plugins overview

what plugins are and the format they follow.

Plugin Launch Flow design doc

the full marketplace → frontend → app server → SDK journey.

software-agent-sdk#3758

(SDK v1.29.0) - the secret expansion behind “Loading a private plugin”.