API Reference

The Centter API is served at /api/v1. All endpoints return JSON. Authenticated endpoints require a Bearer JWT token in the Authorization header.

Health

GET /api/v1/health

Health check. No authentication required.

// Response 200
{
  "status": "ok",
  "version": "0.2.0",
  "uptime": 3600
}

Auth

POST /api/v1/auth/register

Create a new user account.

AuthNone
Status201 Created, 400 Missing fields, 409 Email exists
// Request
{ "email": "user@example.com", "name": "Alice", "password": "secret123" }

// Response 201
{
  "token": "eyJ...",
  "user": { "id": "uuid", "email": "user@example.com", "name": "Alice" }
}

POST /api/v1/auth/login

Authenticate and receive a JWT token (7-day expiry).

AuthNone
Status200 OK, 401 Invalid credentials
// Request
{ "email": "user@example.com", "password": "secret123" }

// Response 200
{
  "token": "eyJ...",
  "user": { "id": "uuid", "email": "user@example.com", "name": "Alice" }
}

POST /api/v1/auth/api-key

Generate an API key for programmatic access.

AuthBearer JWT
Status200 OK
// Response 200
{ "api_key": "amk_...", "user_id": "uuid" }

Networks

POST /api/v1/networks

Create a new network. A slug is generated from the name.

AuthBearer JWT
Status201 Created, 400 Missing name, 409 Slug exists
// Request
{ "name": "My Network" }

// Response 201
{ "id": "uuid", "owner_id": "uuid", "name": "My Network", "slug": "my-network", "created_at": "..." }

GET /api/v1/networks

List all networks owned by the authenticated user.

AuthBearer JWT
Status200 OK
// Response 200
[
  { "id": "uuid", "name": "My Network", "slug": "my-network", "agent_count": "3", "team_count": "1", ... }
]

GET /api/v1/networks/:id

Get network details including agent count, team count, and unassigned agent count.

AuthBearer JWT
Status200 OK, 404 Not found
// Response 200
{
  "id": "uuid", "name": "My Network", "slug": "my-network",
  "agent_count": "7", "team_count": "1", "unassigned_count": "0",
  "transport_mode": "both", "smart_routing": true,
  "previous_transport_config": null, ...
}

PUT /api/v1/networks/:id

Update network settings. When transport_mode changes, saves previous config and recalculates all routes.

AuthBearer JWT
Status200 OK, 400 Invalid transport_mode, 404 Not found
// Request
{ "transport_mode": "http_jwt" }

// Response 200 (when transport changes)
{
  "id": "uuid", "transport_mode": "http_jwt",
  "routing_summary": { "vpc_internal": 4, "tailscale": 0, "public_https": 6 }
}

GET /api/v1/networks/:id/transport

Get transport configuration and agent counts by transport mode.

AuthBearer JWT
Status200 OK, 404 Not found
// Response 200
{
  "transport_mode": "both",
  "require_jwt": false,
  "jwks_url": "/api/v1/networks/uuid/.well-known/jwks.json",
  "agent_count_by_transport": { "tailscale": 4, "http_jwt": 2 }
}

GET /api/v1/networks/:id/routing-table

Get the full routing table with summary counts by route type.

AuthBearer JWT
Status200 OK, 404 Not found
// Response 200
{
  "network": { "id": "uuid", "name": "Coinsenda Agents", "smart_routing": true },
  "summary": { "vpc_internal": 12, "tailscale": 42, "public_https": 0 },
  "routes": [
    { "source": "Linda", "target": "Sol", "type": "vpc_internal", "endpoint": "http://10.10.1.20:8080/a2a", "jwt_required": false }
  ]
}

GET /api/v1/networks/:id/dns

List all DNS records for agents in a network.

AuthBearer JWT
Status200 OK, 404 Not found
// Response 200
{
  "network": "Coinsenda Agents",
  "domain": "coinsenda.mesh.coinsenda.ai",
  "records": [
    { "agent_id": "uuid", "agent": "Linda", "fqdn": "linda.coinsenda.mesh.coinsenda.ai", "ip": "54.1.2.3", "status": "active" }
  ]
}

GET /api/v1/networks/:id/discover

Discover agents in a network. Filter by capability, status, transport, role, or skill name. Returns agents from the NATS registry (falls back to DB).

AuthBearer JWT
Querycapability, status (online|offline|any), transport (nats|http|any), role, skill
Status200 OK, 404 Not found
// GET /api/v1/networks/:id/discover?capability=deploy&status=online

// Response 200
{
  "agents": [
    { "id": "uuid", "name": "Sol", "status": "online", "skills": ["coinsenda-devops"], "transport": "nats", "model": "claude-sonnet-4-6" }
  ],
  "query": { "capability": "deploy", "status": "online", "transport": null, "role": null, "skill": null },
  "total": 1
}

GET /api/v1/networks/:id/presence

Real-time agent presence from NATS (not DB). Shows which agents are online, their transport, and last heartbeat.

AuthBearer JWT
Status200 OK, 404 Not found
// Response 200
{
  "agents": [
    { "id": "uuid", "name": "Linda", "status": "online", "transport": "nats", "last_heartbeat": "2026-03-06T19:25:00Z" }
  ],
  "online_count": 7,
  "total_count": 7
}

Agents

POST /api/v1/networks/:networkId/agents

Register a new agent in a network.

AuthBearer JWT
Status201 Created, 400 Missing name, 404 Network not found
// Request
{ "name": "translator", "description": "Translates text", "endpoint_url": "https://agent.example.com" }

// Response 201
{
  "id": "uuid", "network_id": "uuid", "name": "translator", "status": "active",
  "api_key": "amk_a1b2c3...64hex",  // returned ONCE — save immediately
  ...
}

Note: The api_key is returned only at creation time. Store it securely — it cannot be retrieved later. Only the SHA-256 hash is persisted.

POST /api/v1/agents/:id/regenerate-key

Generate a new API key for an agent, invalidating the previous key.

AuthBearer JWT
Status201 Created, 404 Agent not found
// Response 201
{ "api_key": "amk_...", "agent_id": "uuid" }

The new key is returned once. The old key is immediately invalidated.

GET /api/v1/networks/:networkId/agents

List all agents in a network.

AuthBearer JWT
Status200 OK

GET /api/v1/agents/:id

Get agent details.

AuthBearer JWT
Status200 OK, 404 Not found

PUT /api/v1/agents/:id

Update agent fields. All fields are optional (uses COALESCE).

AuthBearer JWT
Status200 OK, 404 Not found
// Request (all optional)
{ "name": "...", "description": "...", "endpoint_url": "...", "tailscale_ip": "...", "status": "...", "agent_card": {...} }

DELETE /api/v1/agents/:id

Delete an agent permanently.

AuthBearer JWT
Status204 No Content, 404 Not found

POST /api/v1/agents/:id/heartbeat

Agent heartbeat — updates last_seen_at.

AuthX-Agent-Key header
Status200 OK, 401 Missing key, 404 Agent not found
// Response 200
{ "ok": true }

GET /api/v1/agents/:id/peers

Get routes to all peer agents in the same network. Uses cached routes if available, otherwise calculates on-the-fly.

AuthBearer JWT
Status200 OK, 404 Agent not found
// Response 200
{
  "agent": { "id": "uuid", "name": "Linda" },
  "peers": [
    {
      "agent_id": "uuid",
      "name": "Sol",
      "routes": [
        { "route_type": "vpc_internal", "endpoint": "http://10.10.1.20:8080/a2a", "jwt_required": false, "priority": 0 },
        { "route_type": "tailscale", "endpoint": "http://100.101.45.76:8080/a2a", "jwt_required": false, "priority": 1 }
      ],
      "best_route": { "route_type": "vpc_internal", "endpoint": "http://10.10.1.20:8080/a2a", "jwt_required": false }
    }
  ]
}

POST /api/v1/agents/:id/token

Issue a long-lived agent-to-agent token for direct communication.

AuthBearer JWT
Status200 OK, 404 Agent not found
// Request
{ "target_agent_id": "uuid", "scopes": ["skill:execute:translate"] }

// Response 200
{ "token": "eyJ...", "expires_in": 86400 }

GET /api/v1/agents/:id/connections

Get ego-centric connection graph showing peer cards with routes, scopes, and skills.

AuthBearer JWT
Status200 OK, 404 Agent not found
// Response 200
{
  "agent": { "id": "uuid", "name": "Linda", "skills": ["weather", "search"] },
  "peers": [
    {
      "id": "uuid", "name": "Sol",
      "route": { "type": "vpc_internal", "endpoint": "http://10.10.1.20:8080/a2a" },
      "scopes": ["skill:execute:*"],
      "skills": ["coding-agent"]
    }
  ]
}

GET /api/v1/agents/:id/card

Get agent's A2A Agent Card (public, no auth).

AuthNone
Status200 OK, 404 Not found
// Response 200 (custom card or fallback)
{ "name": "translator", "description": "...", "url": "https://agent.example.com", "version": "1.0" }

POST /api/v1/agents/:id/dns

Create or update DNS record for an agent. Sets the agent's subdomain to the specified IP.

AuthBearer JWT
Status200 OK, 400 Invalid IP, 404 Agent not found
// Request
{ "ip": "52.14.50.118" }

// Response 200
{ "fqdn": "sol.coinsenda-agents.mesh.coinsenda.ai", "ip": "52.14.50.118", "status": "ok" }

DELETE /api/v1/agents/:id/dns

Remove DNS record for an agent.

AuthBearer JWT
Status200 OK, 404 Agent not found
// Response 200
{ "status": "removed" }

POST /api/v1/agents/:id/message

Send a message to another agent. Server relays via NATS if target is connected, otherwise queues for HTTP delivery.

AuthBearer JWT
Status201 Created, 400 Validation, 403 No permission, 404 Agent not found
// Request
{ "to": "target-agent-uuid", "text": "Hello from Sol" }

// Response 201
{ "ok": true, "message_id": "uuid", "transport": "nats", "delivered": true }

Routing

Routing endpoints are under the Networks section: GET /networks/:id/routing-table, GET /networks/:id/presence, PUT /networks/:id (transport mode), and GET /networks/:id/transport.

Route types: nats (priority -1, no JWT), vpc_internal (priority 0, no JWT), tailscale (priority 1, no JWT), public_https (priority 2, JWT required).

Permissions

GET /api/v1/scope-templates

Get available scope templates for quick permission assignment.

AuthNone
Status200 OK
// Response 200
[
  { "pattern": "skill:execute:*", "description": "Execute any skill" },
  { "pattern": "skill:read:*", "description": "Read any skill data" },
  { "pattern": "infra:*", "description": "Full infrastructure access" },
  { "pattern": "newsletter:send", "description": "Send newsletters" },
  { "pattern": "*", "description": "Full access (admin)" }
]

POST /api/v1/networks/:networkId/permissions

Grant a permission between two agents.

AuthBearer JWT
Status201 Created, 400 Missing fields, 409 Duplicate
// Request
{ "requester_agent_id": "uuid", "target_agent_id": "uuid", "scope": "skill:execute:translate" }

// Response 201
{ "id": "uuid", "network_id": "uuid", "requester_agent_id": "...", "target_agent_id": "...", "scope": "..." }

GET /api/v1/networks/:networkId/permissions

List permissions in matrix format (grouped by requester → target).

AuthBearer JWT
Status200 OK
// Response 200
{
  "permissions": [
    {
      "requester": { "id": "uuid", "name": "Agent A" },
      "target": { "id": "uuid", "name": "Agent B" },
      "scopes": ["skill:execute:*", "skill:read:*"],
      "permission_ids": ["uuid1", "uuid2"]
    }
  ],
  "raw": [...]
}

GET /api/v1/role-scopes

Get all role templates and their default scopes. Used for role-based auto-grant.

AuthNone
Status200 OK
// Response 200
[
  { "role": "assistant", "scopes": ["skill:execute:*", "skill:read:*"] },
  { "role": "developer", "scopes": ["skill:execute:*", "skill:read:*", "skill:write:*", "infra:*"] },
  { "role": "analyst", "scopes": ["skill:read:*"] }
]

GET /api/v1/networks/:networkId/agents/:agentId/effective-permissions

Get an agent's effective permissions — all granted scopes from roles and manual grants.

AuthBearer JWT
Status200 OK, 404 Agent/Network not found
// Response 200
{
  "agent_id": "uuid",
  "permissions": [
    { "scope": "skill:execute:*", "source": "role:developer", "auto_granted": true },
    { "scope": "newsletter:send", "source": "manual", "auto_granted": false }
  ]
}

DELETE /api/v1/permissions/:id

Revoke a permission.

AuthBearer JWT
Status204 No Content, 404 Not found

Authority

GET /api/v1/networks/:networkId/.well-known/jwks.json

Public JWKS endpoint for token verification.

AuthNone
Status200 OK
// Response 200
{
  "keys": [
    { "kty": "EC", "crv": "P-256", "x": "...", "y": "...", "kid": "uuid", "use": "sig" }
  ]
}

POST /api/v1/networks/:networkId/auth/token

Issue an ES256-signed JWT token for an agent. Scopes are filtered against granted permissions. The token is signed with the network's EC P-256 authority key; verify it via the JWKS endpoint above.

AuthNone (agent API key)
AlgorithmES256 (EC P-256)
Status200 OK, 400 Missing fields, 403 Agent not found/inactive/invalid key
// Request
{ "agent_id": "uuid", "api_key": "amk_...", "scopes": ["skill:execute:translate"] }

// Response 200
{ "token": "eyJ...", "expires_in": 3600 }

// Token header: { "alg": "ES256", "kid": "uuid" }
// Verify via: GET /networks/:networkId/.well-known/jwks.json

NATS JWT + Accounts

Multi-tenant NATS authentication. Each Network maps to a NATS Account, each Agent to a NATS User with role-based permissions.

POST /api/v1/networks/:networkId/nats/account

Create a NATS account for a network. One account per network — enables JWT auth for agents.

AuthBearer JWT (network owner)
Status201 Created, 404 Network not found, 409 Already exists
// Response 201
{ "account_name": "net-a1b2c3d4-My_Network", "account_key": "AABC..." }

POST /api/v1/agents/:id/nats/credentials

Generate NATS credentials (.creds file) for an agent. Returned once — save immediately. Role-based permissions embedded.

AuthBearer JWT (network owner)
Status201 Created, 400 No NATS account, 403 Not authorized, 404 Not found
// Response 201
{ "user_name": "agent-a1b2c3d4-Linda", "public_key": "UABC...",
  "role": "coordinator", "permissions": { "pub": [...], "sub": [...] },
  "creds": "-----BEGIN NATS USER JWT-----\n..." }

PUT /api/v1/agents/:id/nats/permissions

Update agent NATS permissions by role. Returns new creds — agent must reconnect.

AuthBearer JWT (network owner)
Body{"role": "developer"} — coordinator, developer, analyst, support, assistant
Status200 OK, 400 Invalid role / no creds, 403 Not authorized

DELETE /api/v1/agents/:id/nats/credentials

Revoke agent NATS access immediately.

AuthBearer JWT (network owner)
Status200 OK, 400 No creds, 403 Not authorized

GET /api/v1/networks/:networkId/nats/status

NATS account status — shows JWT vs token auth per agent, migration progress.

AuthBearer JWT (network owner)
Status200 OK
// Response 200
{ "status": "configured", "auth_mode": "mixed",
  "summary": { "total": 7, "jwt_auth": 5, "token_auth": 2, "migration_complete": false } }

POST /api/v1/networks/:networkId/authority/rotate

Rotate the network's authority signing keys (EC P-256).

AuthBearer JWT
Status201 Created, 404 Network not found
// Response 201
{ "kid": "uuid", "message": "Key rotated successfully" }

Audit

GET /api/v1/networks/:networkId/audit

Query audit log entries with pagination and filters.

AuthBearer JWT
Queryaction, requester, from, to, limit (max 200), offset
Status200 OK, 404 Network not found
// Response 200
{
  "entries": [
    { "id": "uuid", "action": "permission.grant", "requester": "uuid", "target": "uuid", "scopes": [...], "result": "granted", "ip": "...", "created_at": "..." }
  ],
  "total": 42,
  "limit": 50,
  "offset": 0
}

GET /api/v1/networks/:networkId/audit/export

Export audit log as CSV.

AuthBearer JWT
Queryaction, requester, from, to
Status200 OK (text/csv)
Content-Type: text/csv
Content-Disposition: attachment; filename=audit-log.csv

timestamp,action,requester,target,scopes,result,ip
"2025-01-01T00:00:00.000Z","permission.grant","uuid","uuid","skill:execute:*","granted","127.0.0.1"

Teams

POST /api/v1/teams

Create a team. Auto-creates a network if network_id is not provided.

AuthBearer JWT
Status201 Created, 400 Missing name / BYOK needs key, 404 Network not found
// Request
{
  "name": "My Team",
  "plan": "starter",
  "api_key_mode": "byok",
  "anthropic_api_key": "sk-ant-...",
  "network_id": "uuid"  // optional
}

// Response 201
{ "id": "uuid", "name": "My Team", "slug": "my-team-abc123", "plan": "starter", "api_key_mode": "byok", "anthropic_api_key_encrypted": "***", "network_name": "...", ... }

GET /api/v1/teams

List teams. Optionally filter by ?network_id=uuid.

AuthBearer JWT
Status200 OK
// Response 200
[
  { "id": "uuid", "name": "My Team", "plan": "starter", "agent_count": "3", "network_name": "...", ... }
]

GET /api/v1/teams/:id

Team detail with list of agents.

AuthBearer JWT
Status200 OK, 404 Not found
// Response 200
{
  "id": "uuid", "name": "My Team", ...,
  "agents": [
    { "id": "uuid", "agent_name": "Bot A", "role": "assistant", "deploy_status": "ready", ... }
  ]
}

PUT /api/v1/teams/:id

Update team settings (name, API key mode).

AuthBearer JWT
Status200 OK, 404 Not found
// Request (all optional)
{ "name": "New Name", "api_key_mode": "byok", "anthropic_api_key": "sk-ant-..." }

DELETE /api/v1/teams/:id

Soft-delete a team. All agents are marked as terminated.

AuthBearer JWT
Status204 No Content, 404 Not found

Team Agents

GET /api/v1/templates/roles

Get available agent role templates (assistant, sales, support, developer, analyst, coordinator).

AuthNone
Status200 OK

GET /api/v1/plan-limits

Get agent limits per plan tier.

AuthNone
Status200 OK
// Response 200
{
  "starter": { "maxAgents": 2 },
  "team": { "maxAgents": 5 },
  "business": { "maxAgents": 15 },
  "enterprise": { "maxAgents": 999 }
}

POST /api/v1/teams/:teamId/agents

Add a new agent to a team. Validates role, plan limits, and optional Telegram bot token.

AuthBearer JWT
Status201 Created, 400 Invalid, 403 Plan limit, 404 Team not found
// Request
{ "name": "Sales Bot", "role": "sales", "telegram_bot_token": "123:ABC...", "personality": "Custom soul text", "model": "claude-sonnet-4-20250514" }

// Response 201
{ "id": "uuid", "team_id": "uuid", "agent_id": "uuid", "role": "sales", "deploy_status": "pending", "agent_name": "Sales Bot", ... }

POST /api/v1/teams/:teamId/agents/assign

Assign an existing agent (from the same network) to a team.

AuthBearer JWT
Status201 Created, 404 Agent not found, 409 Already assigned, 403 Plan limit
// Request
{ "agent_id": "uuid" }

// Response 201
{ "id": "uuid", "deploy_status": "ready", "agent_name": "...", ... }

DELETE /api/v1/teams/:teamId/agents/:id/unassign

Remove agent from team without terminating it. Agent stays in the network.

AuthBearer JWT
Status204 No Content, 404 Not found

POST /api/v1/teams/:teamId/agents/:id/deploy

Trigger agent deployment (EC2 provisioning). Agent must be in pending or failed state.

AuthBearer JWT
Status200 OK, 400 Already deployed, 404 Not found
// Response 200
{ "id": "uuid", "deploy_status": "provisioning" }

GET /api/v1/teams/:teamId/agents/:id/status

Get deploy status and logs for a team agent.

AuthBearer JWT
Status200 OK, 404 Not found
// Response 200
{
  "id": "uuid", "deploy_status": "ready", "agent_name": "Bot",
  "logs": [
    { "step": "provisioning", "status": "completed", "message": "..." },
    { "step": "installing", "status": "completed", "message": "..." }
  ]
}

GET /api/v1/teams/:teamId/agents/:id/logs

Get deployment logs only.

AuthBearer JWT
Status200 OK

GET /api/v1/teams/:teamId/agents/:id/metrics

Get agent metrics: uptime, status (online/degraded/offline/stopped), deploy duration.

AuthBearer JWT
Status200 OK, 404 Not found
// Response 200
{
  "uptime_seconds": 86400,
  "last_seen": "2025-01-15T12:00:00.000Z",
  "status": "online",
  "deploy_duration_seconds": 45,
  "deploy_status": "ready",
  "agent_name": "Sales Bot",
  "model": "claude-sonnet-4-20250514"
}

PUT /api/v1/teams/:teamId/agents/:id/config

Update agent configuration (soul, model, role, name).

AuthBearer JWT
Status200 OK, 404 Not found
// Request (all optional)
{ "soul": "New personality text", "model": "claude-sonnet-4-20250514", "role": "support", "name": "New Name" }

POST /api/v1/teams/:teamId/agents/:id/stop

Stop a running agent. Must be in ready state.

AuthBearer JWT
Status200 OK, 400 Not ready, 404 Not found
// Response 200
{ "id": "uuid", "deploy_status": "stopped" }

POST /api/v1/teams/:teamId/agents/:id/start

Start a stopped agent. Must be in stopped state.

AuthBearer JWT
Status200 OK, 400 Not stopped, 404 Not found
// Response 200
{ "id": "uuid", "deploy_status": "ready" }

POST /api/v1/teams/:teamId/agents/:id/restart

Restart an agent (terminate + reset to pending). Must be in ready, stopped, or failed state.

AuthBearer JWT
Status200 OK, 400 Invalid state, 404 Not found
// Response 200
{ "id": "uuid", "deploy_status": "pending", "message": "Agent reset. Deploy again to restart." }

DELETE /api/v1/teams/:teamId/agents/:id

Terminate an agent (stops EC2 instance, marks as terminated).

AuthBearer JWT
Status204 No Content, 404 Not found

GET /api/v1/teams/:teamId/usage

Get team usage data with cost estimates.

AuthBearer JWT
Queryfrom, to (ISO date, default last 30 days)
Status200 OK, 404 Team not found
// Response 200
{
  "usage": [
    { "team_agent_id": "uuid", "date": "2025-01-15", "uptime_minutes": 60, "heartbeat_count": 12, "agent_name": "Bot" }
  ],
  "summary": {
    "total_uptime_minutes": 1440,
    "total_heartbeats": 288,
    "cost_estimate_usd": "14.40",
    "from": "2024-12-16",
    "to": "2025-01-15"
  }
}

GET /api/v1/teams/:teamId/agents/:id/detail

Full agent detail for the agent detail page — includes skills, permissions, config, and metrics.

AuthBearer JWT
Status200 OK, 404 Not found
// Response 200
{
  "id": "uuid", "agent_name": "Sales Bot", "role": "sales",
  "deploy_status": "ready", "model": "claude-sonnet-4-6",
  "reported_skills": ["weather"], "reported_tools": ["web_search"],
  "permissions": [...], "metrics": { "uptime_seconds": 86400, "status": "online" }
}

POST /api/v1/teams/:teamId/agents/:id/heartbeat

Team agent heartbeat. Updates last_seen_at and increments daily usage.

AuthNone
Status200 OK, 404 Team agent not found
// Response 200
{ "status": "ok" }

Marketplace Skills

GET /api/v1/marketplace/skills

Browse marketplace skills with search, category filter, sorting, and pagination.

AuthNone
Querycategory, search, sort (popular|newest|rating), page, limit
Status200 OK
// Response 200
{
  "skills": [
    { "id": "uuid", "name": "Translate Pro", "slug": "translate-pro", "category": "connectors", "install_count": 150, "rating": "4.50", ... }
  ],
  "total": 42,
  "page": 1,
  "pages": 3
}

GET /api/v1/marketplace/skills/:slug

Get skill detail with versions, reviews, and publisher info.

AuthNone
Status200 OK, 404 Not found
// Response 200
{
  "id": "uuid", "name": "Translate Pro", ...,
  "versions": [{ "version": "2.0.0", "changelog": "..." }],
  "reviews": [{ "rating": 5, "comment": "Great!", "user_name": "Alice" }]
}

POST /api/v1/marketplace/skills

Create a new skill (must be a publisher).

AuthBearer JWT (publisher)
Status201 Created, 400 Missing name, 403 Not publisher, 409 Slug taken
// Request
{
  "name": "Translate Pro",
  "description": "AI translation skill",
  "category": "connectors",
  "scopes_required": ["skill:execute:translate"],
  "pricing_type": "free"
}

PUT /api/v1/marketplace/skills/:id

Update a skill (owner only).

AuthBearer JWT (skill owner)
Status200 OK, 404 Not found

POST /api/v1/marketplace/skills/:id/versions

Publish a new version of a skill.

AuthBearer JWT (skill owner)
Status201 Created, 400 Missing version, 404 Skill not found
// Request
{ "version": "2.0.0", "changelog": "Added batch translation", "package_url": "https://..." }

// Response 201
{ "id": "uuid", "skill_id": "uuid", "version": "2.0.0", "changelog": "..." }

GET /api/v1/marketplace/skills/:id/versions

List all versions of a skill.

AuthNone
Status200 OK

Installs

POST /api/v1/agents/:agentId/skills/install

Install a skill on an agent. Auto-grants permissions for required scopes.

AuthBearer JWT
Status201 Created, 404 Agent/Skill not found, 409 Already installed
// Request
{ "skill_id": "uuid" }

// Response 201
{ "ok": true, "skill": "Translate Pro", "version": "1.0.0" }

DELETE /api/v1/agents/:agentId/skills/:skillId

Uninstall a skill. Revokes auto-granted permissions and decrements install count.

AuthBearer JWT
Status204 No Content, 404 Not found

GET /api/v1/agents/:agentId/skills

List skills installed on an agent.

AuthBearer JWT
Status200 OK, 404 Agent not found
// Response 200
[
  { "agent_id": "uuid", "skill_id": "uuid", "name": "Translate Pro", "installed_version": "1.0.0", "publisher_name": "..." }
]

Reviews

POST /api/v1/marketplace/skills/:id/reviews

Add a review for a skill. Recalculates average rating.

AuthBearer JWT
Status201 Created, 400 Invalid rating, 404 Skill not found, 409 Already reviewed
// Request
{ "rating": 5, "comment": "Great skill!" }

// Response 201
{ "id": "uuid", "skill_id": "uuid", "rating": 5, "comment": "Great skill!" }

GET /api/v1/marketplace/skills/:id/reviews

List reviews for a skill.

AuthNone
Status200 OK

Publishers

POST /api/v1/publishers

Become a publisher (one per user).

AuthBearer JWT
Status201 Created, 400 Missing name, 409 Already publisher / slug taken
// Request
{ "name": "My Org", "bio": "We build AI tools", "website": "https://example.com" }

// Response 201
{ "id": "uuid", "name": "My Org", "slug": "my-org", ... }

GET /api/v1/publishers/:slug

Get public publisher profile with skill count.

AuthNone
Status200 OK, 404 Not found

GET /api/v1/publishers

Get the current user's publisher profile (or null if not a publisher).

AuthBearer JWT
Status200 OK

PUT /api/v1/publishers/:id

Update publisher profile.

AuthBearer JWT (owner)
Status200 OK, 404 Not found
// Request (all optional)
{ "name": "Updated Org", "bio": "...", "website": "..." }