---
name: onei-operator
description: Operate the onei.ai AI directory — review pending app submissions, publish and curate AI skills and MCP servers, manage categories, and feature content via the Onei operations REST API. Use when asked to run, moderate, curate, or publish content on onei.ai.
---

# Onei AI Operator

You can operate https://onei.ai — an AI app / skill / MCP-server directory — through its REST API.

## Setup

You need an API key (format `onei_` + 40 hex chars). It is provided by the site owner (minted at https://onei.ai/admin/api-keys). Send it on every request:

```
Authorization: Bearer onei_xxxxxxxx...
Content-Type: application/json
```

Base URL: `https://onei.ai/api/v1`

Every response is `{"ok": true, "data": ...}` on success or `{"ok": false, "error": {"code", "message", "issues"?}}` on failure. Rate limit: 120 req/min. Scopes: `read` (browse), `write` (create/update/moderate/feature), `admin` (delete + categories).

## Common operations

### Check site health

```bash
curl -s -H "Authorization: Bearer $ONEI_KEY" https://onei.ai/api/v1/stats
```

Returns counts of active/pending apps, skills, MCPs, users, reviews. A growing `apps.pending` number means the moderation queue needs attention.

### Moderate the app submission queue

1. List pending submissions:
   `GET /api/v1/apps?status=pending`
2. Inspect one in full (description, screenshots, provider):
   `GET /api/v1/apps/{slug}`
3. Judge it: real product, working URL, non-spammy description, appropriate category.
4. Approve or reject:
   - `POST /api/v1/apps/{slug}/approve` (publishes it)
   - `POST /api/v1/apps/{slug}/reject` (keeps it unpublished)

### Publish a new AI app

`POST /api/v1/apps` — images are given as remote URLs and sideloaded to the CDN:

```json
{
	"name": "Example AI",
	"url": "https://example.ai",
	"iconUrl": "https://example.ai/icon.png",
	"summary": "10-200 chars shown on cards",
	"description": "At least 50 chars of markdown",
	"categoryId": 1,
	"pricing": "freemium",
	"providerName": "Example Inc",
	"providerUrl": "https://example.ai",
	"screenshotUrls": ["https://example.ai/shot1.png"]
}
```

The slug is derived from `url` (e.g. `example.ai`) and is permanent. The app goes live immediately unless you pass `"pending": true`. Check `GET /api/v1/apps/categories` for valid `categoryId` values, and search first (`GET /api/v1/apps?status=all&q=...`) to avoid duplicates. Update with `PATCH /api/v1/apps/{slug}`; delete (admin scope) with `DELETE /api/v1/apps/{slug}`.

### Feature / rank content

- Feature an app and optionally set its discovery weight (0-100, higher = more prominent in "Trending"):
  `POST /api/v1/apps/{slug}/feature` with `{"featured": true, "weight": 80}`
- Feature a skill or MCP (pins it first in listings):
  `POST /api/v1/skills/{slug}/feature` / `POST /api/v1/mcps/{slug}/feature` with `{"featured": true}`

### Publish a new AI skill

`POST /api/v1/skills` with at minimum:

```json
{
	"name": "Code Review Assistant",
	"slug": "code-review-assistant",
	"summary": "10-300 chars shown on cards",
	"description": "Longer markdown description",
	"author": "Author Name",
	"content": "# The actual skill prompt/definition...",
	"contentFormat": "markdown",
	"tags": ["review"],
	"features": ["Finds bugs"],
	"categoryIds": [1],
	"isActive": true
}
```

Optional per-client usage docs: `usageClaudeDesktop`, `usageCursor`, `usageWindsurf`, `usageVscode`, `usageGeneric` (markdown strings), plus `exampleUsage`, `repositoryUrl`, `documentationUrl`, `version`, `license`, `iconUrl`.

Update with `PATCH /api/v1/skills/{slug}` (partial body). Delete (admin scope) with `DELETE /api/v1/skills/{slug}`.

### Publish a new MCP server

`POST /api/v1/mcps` — same meta fields as skills (no `content`), plus `npmPackage` and per-client install configs:

```json
{
	"name": "Filesystem MCP",
	"slug": "filesystem-mcp",
	"summary": "...",
	"description": "...",
	"author": "...",
	"npmPackage": "@modelcontextprotocol/server-filesystem",
	"installClaudeDesktop": {
		"command": "npx",
		"args": ["-y", "@modelcontextprotocol/server-filesystem", "/path"],
		"env": { "OPTIONAL": "vars" }
	},
	"installCustom": [{ "command": "docker", "args": ["run", "..."], "description": "Docker" }]
}
```

`installCursor`, `installWindsurf`, `installVscode` follow the same config shape.

### Manage categories (admin scope)

- List: `GET /api/v1/{apps|skills|mcps}/categories`
- Create: `POST /api/v1/skills/categories` with `{"name", "slug", "summary", "iconName"?}` (app categories take `{"name", "summary"}` only)
- Update: `PATCH /api/v1/{skills|mcps}/categories/{slug}`
- Delete: `DELETE /api/v1/{skills|mcps}/categories/{slug}` — refused (409) while the category still has entries.

## Rules of thumb

- Slugs are permanent URLs — pick lowercase-kebab-case and don't change them after publishing.
- Never approve an app without fetching its detail first; check that the URL looks legitimate.
- Prefer `PATCH` with only the changed fields over resending the whole object.
- Summaries must be 10-300 characters (apps: 10-200); descriptions at least 10 (apps: at least 50).
- Edge caches purge automatically after each write — no extra step needed; public pages may lag a few minutes at most.
- On `422`, read `error.issues` — it contains per-field validation messages.
