Marketplace developer docs

Build + test + ship skills, integrations, agent templates, workflow templates, and squad templates.

<a id="overview"></a>

Overview

The Enclaw Marketplace lets anyone author and ship five kinds of asset that get installed into a tenant with one click:

KindUsed forCloned-on-install?
SkillA callable tool — wraps an API, runs a transformation, calls an external serviceReference (binds to tenant via tool-registry)
IntegrationAuth + tool family for an external service (Slack, Notion, etc.)Reference + tenant provides config
Agent templatePre-built agent: identity prompt, model, default skillsYes — cloned into agent_templates
Workflow templateFrozen workflow steps + triggerYes — cloned into workflow_templates
Squad templateMulti-agent collaboration recipeYes — cloned into squad_templates

Every listing has:

  • A slug (unique within its kind), title, summary, readme.
  • One or more versions (semver). The latest approved version is what installers get when they click "Install".
  • A kind-specific payload — the JSON snapshot the installer clones.

Once submitted, listings go to pending_review until a platform operator approves them. Approved listings are visible to every tenant on the deployment.

<a id="local-sdk"></a>

Local SDK (@enclaw/marketplace-sdk)

Author + iterate without an Enclaw instance.

npm i -g @enclaw/marketplace-sdk
CommandWhat it does
enclaw-mp validate <manifest.json>Schema check against the top-level shape AND the kind-specific payload. Exit 1 + structured paths on errors.
enclaw-mp test <manifest.json> --input '<json>'Skills only. Imports the entrypoint module and invokes its run(input) function. Pretty-prints the result.
enclaw-mp publish <manifest.json> --api <url> --token <bearer>Creates the listing, uploads the version, submits for moderator review in one shot. --api + --token default to ENCLAW_API_URL + ENCLAW_TOKEN env vars.

Or use it as a library:

import { validate, runSkill, publish } from "@enclaw/marketplace-sdk";

const result = validate(manifest);
if (!result.ok) {
  for (const e of result.errors) console.error(`${e.path}: ${e.message}`);
}

const out = await runSkill("./skill.mjs", { input: "..." });

await publish(manifest, { apiBaseUrl: "https://api.enclaw.example", token: "..." });

<a id="manifest-shape"></a>

Manifest shape (all kinds)

Every kind shares the same top-level wrapper. Only payload shape changes per kind.

{
  "kind": "skill",
  "slug": "my-skill",
  "title": "My Skill",
  "summary": "One-line description.",
  "readmeMd": "# Long-form readme — markdown.",
  "iconUrl": "https://example.com/icon.png",
  "tags": ["productivity", "marketing"],
  "version": "1.0.0",
  "changelogMd": "Initial release",
  "payload": { /* kind-specific */ }
}

Constraints (enforced by the SDK + the API):

  • slug: lowercase letters / digits / hyphens; must not start or end with a hyphen; 3-80 chars. Unique within its kind.
  • version: semver (MAJOR.MINOR.PATCH with optional -prerelease).
  • summary: 500 chars max.
  • tags: small arbitrary strings shown as chips.

<a id="skills"></a>

Authoring a skill

A skill is a single callable function. The marketplace stores the tool schema (so agents can discover + invoke it) and the entrypoint path (so the local SDK can run it for testing).

payload shape

{
  "toolSchema": {
    "name": "do_thing",
    "description": "Run the skill.",
    "input_schema": {
      "type": "object",
      "properties": { "x": { "type": "string" } },
      "required": ["x"]
    }
  },
  "language": "javascript",
  "entrypoint": "./skill.mjs",
  "requiresVision": false
}

Entrypoint contract

A skill exports a run function (or default-exports it). It receives the validated input as a plain object and returns any JSON-serialisable value.

// skill.mjs
export async function run({ csv, hasHeader = true }) {
  const rows = csv.split("\n").filter(Boolean).map((r) => r.split(","));
  if (rows.length === 0) return { markdown: "" };
  const header = hasHeader ? rows.shift() : rows[0].map((_, i) => `col${i + 1}`);
  const md = [
    `| ${header.join(" | ")} |`,
    `| ${header.map(() => "---").join(" | ")} |`,
    ...rows.map((r) => `| ${r.join(" | ")} |`),
  ].join("\n");
  return { markdown: md };
}

Test locally

enclaw-mp validate ./manifest.json
enclaw-mp test ./manifest.json --input '{"csv":"name,age\nalice,30\nbob,25"}'

<a id="integrations"></a>

Authoring an integration

An integration bundles auth + a family of tools that talk to an external service. The marketplace listing describes:

  • The type discriminator (e.g. notion, linear).
  • The configSchema (what the tenant admin types when installing).
  • The list of tools the integration surfaces to agents.

payload shape

{
  "type": "notion",
  "configSchema": {
    "type": "object",
    "required": ["notionToken"],
    "properties": {
      "notionToken": {
        "type": "string",
        "description": "Internal integration secret",
        "secret": true
      }
    }
  },
  "tools": [
    "notion_search_pages",
    "notion_create_page",
    "notion_append_block"
  ]
}

secret: true on a property → the install UI renders a password field and the API stores the value encrypted.

Reference vs cloned: integrations are reference-style. The actual tool implementations live in the Enclaw core; the marketplace listing declares which ones the tenant can use after installing.

<a id="agent-templates"></a>

Authoring an agent template

A template is a frozen agent config that gets cloned into the tenant's agent_templates table on install.

payload shape

{
  "identityMd": "You are a senior copywriter...",
  "modelId": "claude-sonnet-4-6",
  "modelConfig": { "temperature": 0.7 },
  "defaultConfig": {},
  "includedSkills": [
    { "source": "marketplace", "name": "url-readability-extract" }
  ],
  "requiredIntegrations": ["mailchimp"],
  "category": "marketing",
  "icon": null
}

Field reference

FieldWhat
identityMdThe system prompt. Long-form markdown — be specific about role, constraints, output format.
modelIdDefault model. Tenant can override after install.
modelConfigTemperature / top_p / max_tokens overrides.
defaultConfigArbitrary tenant-overridable config (e.g. { sprintLengthDays: 14 }).
includedSkillsMarketplace skills auto-bound on install. source is marketplace / builtin / cli.
requiredIntegrationsListed on the install screen so tenants know they need to configure these first.
categoryOne of: engineering, pm, marketing, hr, support, sales, leadership, general.

Writing identity prompts

  • Lead with role (You are a senior ...).
  • State constraints explicitly (NEVER auto-approve. Always cite line numbers.).
  • Specify output format (Output: { fit: 0-100, reasoning: ... }).
  • Add examples of good behaviour if behaviour is non-obvious.

<a id="workflow-templates"></a>

Authoring a workflow template

A workflow is a sequence of steps. Each step has a type and type-specific config. The full step shape comes from the @enclaw/workflow-ui types.

payload shape

{
  "steps": [
    {
      "id": "fetch_issues",
      "type": "tool",
      "tool": "linear_search_issues",
      "input": { "filter": "completed", "sinceDays": 7 }
    },
    {
      "id": "summarise",
      "type": "llm",
      "prompt": "Draft a weekly update from {{fetch_issues}}. 3-5 bullets max."
    },
    {
      "id": "post",
      "type": "tool",
      "tool": "slack_send_message",
      "input": { "channel": "#product-updates", "text": "{{summarise}}" }
    }
  ],
  "trigger": {
    "type": "cron",
    "expression": "0 16 * * 5",
    "timezone": "Australia/Sydney"
  },
  "requiredSkills": [],
  "requiredIntegrations": ["linear", "slack"],
  "category": "pm"
}

Step types

typeWhat it does
toolCall any tool (builtin / installed skill / integration).
llmOne-shot LLM call with a prompt template.
transformJSONata expression over previous step outputs.
branchIf/else routing (PR-C feature; reference only in v1).
loopforEach / repeat-N.
approvalPause for human approval.
subflowCall another workflow.
agentDelegate to an agent.

Variable references

Inside step config, reference previous step outputs with {{stepId}} or property paths {{stepId.field}}. The trigger payload is {{trigger}}.

<a id="squad-templates"></a>

Authoring a squad template

A squad is a group of agents that collaborate. The template describes roles + collaboration rules.

payload shape

{
  "members": [
    {
      "role": "writer",
      "agentTemplateId": "content-marketer",
      "responsibilities": "Drafts long-form content."
    },
    {
      "role": "reviewer",
      "agentName": "SEO Analyst",
      "responsibilities": "Keyword research + meta optimisation."
    }
  ],
  "rules": {
    "handoff": "writer → reviewer",
    "sharedMemory": true,
    "approvalRequired": ["reviewer"]
  },
  "requiredSkills": ["url-readability-extract"],
  "requiredIntegrations": ["mailchimp"],
  "category": "marketing"
}

Member resolution

  • agentTemplateId (preferred) → on install, the marketplace clones that agent-template listing into the tenant's agent_templates and binds the squad member to it.
  • agentName → tenant is prompted to pick an existing agent matching this role on install.

<a id="publishing"></a>

Publishing + moderation

  1. Run enclaw-mp validate ./manifest.json until clean.
  2. (Skills only) enclaw-mp test ./manifest.json --input '...' until happy.
  3. enclaw-mp publish ./manifest.json --api $ENCLAW_API_URL --token $ENCLAW_TOKEN.
  4. Status flips to pending_review. Track it under My Listings.
  5. A platform operator approves or rejects via the operator console at /admin/saas/marketplace in the web app.
  6. On approve → status flips to approved and the listing appears in the public catalog. Reviewer notes (if any) are visible to you in the My Listings dashboard.

Versioning

Subsequent releases:

# Bump version in manifest.json, edit changelogMd, then:
enclaw-mp publish ./manifest.json

The new version goes to pending and only becomes the "latest" once an operator approves. Old approved versions remain installable — tenants on an old version don't break when you ship a new one.

Deprecation

If you want to retire a listing (it shouldn't get new installs but existing installs keep working), the publisher (or a platform admin) can hit:

POST /api/marketplace/listings/{id}/deprecate

Existing installs are unaffected; the listing disappears from search.

<a id="examples"></a>

Worked examples

Real listings already in the marketplace ship with full readmes and payloads — use them as templates:

  • CSV → Markdown Table (skill, JS, single file) → /listings filter by Skills.
  • Sprint Planner (agent template, Linear required) → filter by Agents.
  • Weekly Product Update (workflow, cron-triggered) → filter by Workflows.
  • Marketing Launch Crew (squad, 3 members) → filter by Squads.

Browse all listings →