Building

API reference

The REST endpoints for publishing and installing plugins, and the webhook address plugin channels receive on.

Everything on this page is also a button in the console. Use the API to publish from a script or CI.

Base URL: https://services.agentparley.ai/platform

Authentication: Authorization: Bearer <token> — the same signed-in user token the console uses. Every request acts as that user on that user's account. Ids in requests and responses are strings.

Authoring

Create a plugin

POST /plugins
Content-Type: application/json

{ "slug": "weather", "displayName": "Weather", "description": "Forecasts for your agents." }

201 Created with the plugin, including its id. The slug must be unused.

List and read your plugins

GET /plugins
GET /plugins/{id}?includeFiles=true

The detail includes every version (semVer, status, sourceSha256), visibility, and — with includeFiles — the latest files.

Publish a version from a zip

POST /plugins/{id}/versions/zip
Content-Type: multipart/form-data

file=@weather.zip
semVer=1.0.0

The zip holds the package — readme.md is required — optionally inside one wrapper folder. Up to 256 MB. This is the only way to publish binary assets (companion packs).

curl -X POST "$BASE/plugins/$PLUGIN_ID/versions/zip" \
  -H "Authorization: Bearer $TOKEN" \
  -F "file=@weather.zip;type=application/zip" \
  -F "semVer=1.0.1"
Response Meaning
201 published; the body summarizes the version and what was discovered (commands, skills, middleware, lifecycle, tools, providers)
other 4xx a validation error — the message says what and where
409 that version already exists (versions are immutable)
503 bundler busy — retry shortly

Publish a version from a file list

POST /plugins/{id}/versions
Content-Type: application/json

{
  "semVer": "1.0.1",
  "files": [
    { "path": "readme.md", "contentText": "---\nname: Weather\n---\n# Weather\n…" },
    { "path": "index.ts", "contentText": "globalThis.tools.get_forecast = { … };" }
  ]
}

Text files only.

Save a draft

POST /plugins/{id}/draft
Content-Type: application/json

{ "files": [ … ] }

Builds and validates like a publish, without a version number. Installs on the Draft policy (your own account only) follow it.

List in Browse / remove from Browse

POST   /plugins/{id}/listing
DELETE /plugins/{id}/listing        { "deprecationMessage": "Replaced by weather-pro." }

Listing requires a published version. Removing keeps existing installs working and shows them the message.

Yank a version / delete a plugin

POST   /plugins/{id}/versions/{versionId}/yank
DELETE /plugins/{id}

You can't yank the last version; you can't delete a plugin that still has a published version.

Catalog and installs

GET /marketplace                     every listed plugin, plus your own private ones
GET /marketplace/{slug}              readme, capabilities, what it can reach, installed or not
POST   /installations                { "slug": "weather", "versionPolicy": "Latest" }
GET    /installations
GET    /installations/{id}
POST   /installations/{id}/configure { "settings": { "units": "metric" }, "secrets": { "apiKey": "…" } }
PATCH  /installations/{id}           { "versionPolicy": "Pinned", "pinnedVersionId": "…" }
POST   /installations/{id}/accept-update  { "versionId": "…" }
POST   /installations/{id}/rerun-setup
DELETE /installations/{id}
  • versionPolicy: Latest (default), Pinned, or Draft (your own plugin only).
  • configure takes setting values as strings; secrets are write-only and never returned.
  • accept-update accepts a version that asks for more access.
  • rerun-setup sends pluginInstalled again (after a plan upgrade, for example).
  • DELETE uninstalls: the plugin's agents (and their chats), channels, live connections and stored secrets are removed, and pluginUninstalled is sent.

Logs

GET /plugin-logs?installationId={id}&levels=warn&levels=error&limit=100&cursor=…

Your plugin's host.log lines for an installation (optionally one sessionId), paged with cursor, kept 24 hours.

Inbound webhooks for plugin channels

A channel plugin that receives by webhook gets one address per connected channel:

https://webhook.agentparley.ai/webhook/plugin/{routingId}

The channel's page in the console shows it (and can replace it with a new address). Everything the vendor POSTs there reaches your channel.receive — headers, the text body, and the exact bytes for signature checks. Requests are queued on arrival, so messages sent while AgentParley deploys are delivered afterwards rather than bounced.