# OpenWork Marketplace Architecture Explained: How to Publish Plugins and Share Skills Across Teams

> Discover the OpenWork marketplace architecture. Learn how to publish plugins and share skills across teams with its Den control-plane API, marketplace objects, and automatic synchronization.

- Repository: [Different AI/openwork](https://github.com/different-ai/openwork)
- Tags: architecture
- Published: 2026-08-01

---

**OpenWork's marketplace architecture consists of a Den control-plane API for storing plugin metadata, marketplace objects that organize skills and MCP connections, team-based access controls, and automatic client-side synchronization that delivers published plugins to teammates without manual installation.**

The OpenWork marketplace is the central hub that makes **skills, plugins, MCP connections, and LLM providers** discoverable and shareable across a team or entire organization. In `packages/openwork-bootstrap/bin/openwork.mjs` (lines 571-615), the desktop client implements the marketplace sync logic that pulls available catalogs on startup. This article breaks down the architecture using concrete source code references and shows exactly how to publish plugins through both the UI and programmatically.

## How the OpenWork Marketplace Architecture Works

The architecture follows a **cloud-first, publish-once, consume-anywhere** model. Five layers work together to deliver plugins from authors to end users.

### Den Control-Plane API

The server-side service stores all marketplace metadata. It exposes REST endpoints consumed by the desktop client and automation scripts.

According to [`packages/docs/openapi.json`](https://github.com/different-ai/openwork/blob/main/packages/docs/openapi.json), the marketplace-related paths include:
- `GET /v1/marketplaces` — list accessible marketplaces
- `POST /v1/marketplaces` — create new marketplace
- `GET /v1/plugins` and `POST /v1/plugins` — plugin operations

The OpenAPI specification defines the complete JSON schema for marketplace and plugin objects, including required fields like `name`, `description`, `marketplaceId`, and nested `skills` arrays.

### Marketplace Model Structure

A **marketplace object** holds a collection of **plugins**. Each plugin bundles:

| Component | Purpose | Schema Location |
|-----------|---------|---------------|
| **Skills** | Runnable runbooks (executable code) | `plugin.skills[]` in OpenAPI spec |
| **MCP definitions** | External service connections (Slack, Google, etc.) | `plugin.mcpConnections[]` |
| **Metadata** | Name, description, version, author info | Top-level `plugin` properties |

The `marketplaceId` field links every plugin to its parent marketplace. This hierarchical structure enables scoped permission grants at the marketplace level.

### Desktop Client Integration

The Electron/React UI reads marketplace data on startup. In `openwork.mjs`, the client-side request helper implements the sync:

```javascript
// From packages/openwork-bootstrap/bin/openwork.mjs (lines ~571-615)
// Simplified extraction showing the core pattern

async function syncMarketplaces(apiBase) {
  const response = await request(apiBase, '/v1/marketplaces', {
    method: 'GET',
  });
  // Response cached and rendered in Settings → Extensions → Marketplace
  return response.body.items;
}

```

The UI components in `packages/ui/src/react/extensions/` render the marketplace list and plugin creation dialogs. Users experience this as **Settings → Extensions → Marketplace**.

### Team-Based Access Control

Organizations use **teams** and **roles** to gate marketplace visibility. The Den API enforces permissions through endpoints like `/v1/organizations/:orgId/teams/:teamId/access`.

As documented in `packages/docs/cloud/team-quickstart.mdx`, access grants are cached client-side. When a teammate's team gains marketplace access, plugins appear automatically—no reinstall required.

### Cloud-Only Delivery Model

Once published to a marketplace, plugins propagate to all authorized clients on next startup. This behavior is illustrated in [`evals/voiceovers/marketplace-connect-only-delivery.md`](https://github.com/different-ai/openwork/blob/main/evals/voiceovers/marketplace-connect-only-delivery.md): the author publishes once, and teammates see the plugin immediately in their desktop UI.

## How to Publish Plugins to the OpenWork Marketplace

Publishing follows four steps whether using the Cloud dashboard or desktop app.

### Step 1: Create an Organization Marketplace

If no marketplace exists for your team:

1. Navigate to **Cloud dashboard → Extensions → Marketplaces**
2. Click **New marketplace**, enter a name, save
3. The UI calls `POST /v1/marketplaces` with your organization context

The marketplace entry persists in Den's control plane with a unique `id`.

### Step 2: Create a Plugin Inside the Marketplace

In the desktop app:

1. Go to **Extensions → Plugins**, click **New plugin**
2. Select your marketplace from the dropdown (pre-selected if only one exists)
3. Fill **name**, **description**, and add **skills**:
   - Skill `name`: identifier for invocation
   - Skill `description`: shown in UI tooltips
   - Skill `run`: the executable JavaScript/TypeScript code

4. Optionally attach **MCP connections** for external API access

### Step 3: Save and Publish

Clicking save triggers:

```javascript
// Equivalent HTTP call made by the UI
POST /v1/plugins
Content-Type: application/json

{
  "name": "Sales-Call-Prep",
  "description": "Generate agendas from briefs",
  "marketplaceId": "mkt_abc123xyz",
  "skills": [
    {
      "name": "generateAgenda",
      "description": "Creates bullet-point agenda",
      "run": "function run(context) { /* implementation */ }"
    }
  ],
  "mcpConnections": []
}

```

Success returns HTTP 201 with the plugin `id`. The plugin is immediately visible to teammates with marketplace access.

### Step 4: Grant Access (Optional)

For fine-grained control:

- **Extensions → Marketplaces → [Your Marketplace] → Members**
- Add teams or individual users
- Changes propagate on client restart or manual sync

## Programmatic Plugin Publishing

Use the same `request` helper as the CLI for automation. This Node.js example mirrors `openwork.mjs` internals:

```javascript
// publish-plugin.mjs
import { request } from './packages/openwork-bootstrap/bin/openwork.mjs';

const API_BASE = 'https://api.openworklabs.com';

// 1. Locate or verify marketplace access
const markets = await request(API_BASE, '/v1/marketplaces', { method: 'GET' });
if (markets.status !== 200) throw new Error(`List failed: ${markets.status}`);

const targetMarket = markets.body.items.find(m => m.name === 'Engineering') 
                  || markets.body.items[0];

// 2. Publish plugin with embedded skill
const publish = await request(API_BASE, '/v1/plugins', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    name: 'Incident-Response-Automation',
    description: 'Auto-create PagerDuty incidents from alerts',
    marketplaceId: targetMarket.id,
    skills: [
      {
        name: 'createIncident',
        description: 'Creates PagerDuty incident with context',
        run: `
          function run(ctx) {
            const { title, severity } = ctx.args;
            // PagerDuty API call implementation
            return { incidentId: 'PD-12345', url: '...' };
          }
        `.trim()
      }
    ],
    mcpConnections: [
      { service: 'pagerduty', scope: 'incidents:write' }
    ]
  })
});

if (publish.status !== 201) {
  throw new Error(`Publish failed: ${JSON.stringify(publish.body)}`);
}

console.log(`Published plugin: ${publish.body.id}`);

```

The `request` function handles authentication, retries, and error formatting identically to the desktop client's marketplace sync (see `openwork.mjs` lines 571-615).

## Marketplace Architecture Flow: Complete Data Path

```

┌─────────────────┐     GET /v1/marketplaces      ┌─────────────────┐
│   Desktop App   │ ◄──────────────────────────── │  Den API Server │
│  (openwork.mjs) │                               │  (OpenAPI spec) │
└────────┬────────┘                               └────────┬────────┘
         │                                                │
         │  Renders marketplace list                      │  Stores:
         │  Settings → Extensions → Marketplace           │  - marketplace records
         ▼                                                │  - plugin definitions
┌─────────────────┐                                       │  - team access grants
│     User UI     │                                       │
│  (packages/ui)  │                                       │
└────────┬────────┘                                       │
         │                                                │
         │  Author publishes plugin                       │
         │  POST /v1/plugins                              │
         └──────────────────────────────────────────────► │
                                                          │
                              ┌─────────────────┐         │
                              │  Team Members   │ ◄───────┘
                              │  (auto-sync on  │   Access check:
                              │   next startup) │   team grants
                              └─────────────────┘

```

## Key Source Files Reference

| File | Purpose |
|------|---------|
| `packages/openwork-bootstrap/bin/openwork.mjs` | Marketplace sync client, request helper, CLI implementation |
| [`packages/docs/openapi.json`](https://github.com/different-ai/openwork/blob/main/packages/docs/openapi.json) | Complete REST API specification for marketplaces and plugins |
| `packages/docs/cloud/team-quickstart.mdx` | Step-by-step UI guide for marketplace creation |
| [`evals/voiceovers/marketplace-connect-only-delivery.md`](https://github.com/different-ai/openwork/blob/main/evals/voiceovers/marketplace-connect-only-delivery.md) | Delivery model documentation and UX scenarios |
| `packages/ui/src/react/extensions/` | React components for marketplace UI (MarketplaceList, PluginEditor) |

## Summary

- **OpenWork marketplace architecture** centers on Den control-plane APIs that store plugin metadata and enforce team-based access controls
- **Marketplace objects** contain **plugins**, which bundle **skills** (runnable code) and optional **MCP connections**
- **Desktop clients** sync marketplace content automatically via `request()` calls in `openwork.mjs` (lines 571-615)
- **Publish-once delivery** means plugins appear for all authorized teammates without manual installation steps
- **Programmatic publishing** uses the same `request` helper and `POST /v1/plugins` endpoint as the UI

## Frequently Asked Questions

### What is the minimum required to publish a plugin?

A marketplace, a name, and at least one skill with a `run` function. The `description` and `mcpConnections` fields are optional. The OpenAPI schema in [`packages/docs/openapi.json`](https://github.com/different-ai/openwork/blob/main/packages/docs/openapi.json) marks only `name`, `marketplaceId`, and `skills` as required.

### How do teammates receive published plugins automatically?

The desktop client calls `GET /v1/marketplaces` on every startup. If a teammate's team has marketplace access, the response includes current plugins. The UI renders these without requiring manual install steps. This sync logic is in `packages/openwork-bootstrap/bin/openwork.mjs`.

### Can plugins be published without the Cloud dashboard?

Yes. Use the programmatic example above with the `request` helper from `openwork.mjs`. Any HTTP client that authenticates to the Den API and calls `POST /v1/plugins` with valid JSON can publish.

### What limits access to a marketplace's plugins?

Team grants. In **Extensions → Marketplaces → Members**, owners add teams or users. The Den API checks these grants on every marketplace list request. Users without grants never receive those plugins in their sync response.