登录
docs/index.tsxpublic api v1.2
RESTOpenAPI 3.1Skills + SOUL

> Skills + SOUL Marketplace API Docs

公开 API 现在同时覆盖 Skill 与 SOUL。Skill 偏可执行能力包,SOUL 偏单文件行为 / 风格 / 策略资产。

readme.flowrecommended path

先在 Dashboard 生成 API Key,再复制示例请求测试搜索与下载链路。

计费公开接口支持匿名访问;提供有效 API Key 时,会提高分钟限流额度并开始计入日配额。

如果你要导入 Postman、Insomnia 或生成 SDK,直接使用 OpenAPI JSON。

quickstart.sh3 steps
01 · 获取密钥

在 Dashboard 创建 `sk_live_*` API Key。也可以先匿名调用搜索接口做联调。

02 · 发起第一个请求
curl example
curl -X GET "https://your-domain.com/api/v1/souls/search?category=writing&q=mentor&sortBy=recent" \
  -H "Authorization: Bearer sk_live_your_api_key"
03 · 处理响应与配额

读取 `success/data/page/limit/total/totalPages/filters`,并同时关注 `X-RateLimit-Minute-*` 与 `X-RateLimit-Daily-*` 响应头。

auth.mdoptional bearer

鉴权

计费公开接口支持匿名访问。你也可以把控制台生成的 API Key 放入 Authorization 头中:

authorization header
Authorization: Bearer sk_live_your_api_key

计费接口不强制鉴权;未提供或无效 API Key 时,会按匿名请求处理。SOUL 详情和分类接口本次不计入日配额。

rate-limit.mdminute + utc daily quota

配额与响应头

匿名计费请求默认每 IP 每分钟 25 次;带有效 API Key 的计费请求提升到每 IP 每分钟 35 次,并同时计入每日配额。

当前计费接口:Skills search、Skills ai-search、SOUL search、SOUL download。

X-RateLimit-Tier

当前应用的分钟级配额层级。

X-RateLimit-Minute-Remaining

当前分钟窗口剩余请求数。

X-RateLimit-Daily-Limit

当前 API Key 的日配额上限。

X-RateLimit-Daily-Remaining

当前 UTC 日剩余成功请求数。

当分钟限流或日配额用尽时,接口返回 `429`,错误码分别为 `MINUTE_RATE_LIMIT_EXCEEDED` 和 `DAILY_QUOTA_EXCEEDED`。

GET /api/v1/skills/searchbrowse + filter

这是公开技能发现接口。你可以只传 `category`、`tag` 做筛选,也可以叠加 `q` 关键词,再用 `sortBy` 控制排序。

qstringrequired:

可选关键词,匹配 name、description、category、tags

categorystringrequired:

按分类筛选,例如 Utilities

tagstringrequired:

按标签筛选,精确匹配单个 tag

pageintegerrequired:

页码,从 1 开始,默认 1

limitintegerrequired:

每页数量,默认 24,最大 100

sortByrecent | popularrequired:

recent 按更新时间倒序,其他值按热度排序

response example
{
  "success": true,
  "data": [
    {
      "id": "cm0abc123xyz",
      "slug": "csv-pipeline",
      "name": "CSV Pipeline",
      "description": "Process, transform, and analyze CSV datasets.",
      "category": "data",
      "tags": ["csv", "etl", "analysis"],
      "status": "APPROVED",
      "filePath": "skills/csv-pipeline/SKILL.md",
      "frontmatter": null,
      "frontmatterRaw": null,
      "downloads": 128,
      "createdAt": "2026-03-01T09:30:00.000Z",
      "updatedAt": "2026-03-08T14:12:00.000Z",
      "authorId": "user_123"
    }
  ],
  "page": 1,
  "limit": 10,
  "total": 42,
  "totalPages": 5,
  "filters": {
    "q": null,
    "category": "Utilities",
    "tag": "search",
    "sortBy": "popular"
  }
}
GET /api/v1/skills/ai-searchexperimental alias

`AI Search` 当前仍是实验性别名接口,返回结构与普通搜索一致,适合作为未来智能检索入口的兼容地址。

curl example
curl -X GET "https://your-domain.com/api/v1/skills/ai-search?q=spreadsheet%20automation&sortBy=recent" \
  -H "Authorization: Bearer sk_live_your_api_key"
GET /api/v1/souls/searchbrowse + filter

这是公开 SOUL 发现接口。SOUL 只支持一级固定分类,不带 tag,适合做轻量人格、策略和风格资产检索。

qstringrequired:

可选关键词,匹配 name、description、category

categoryassistant | workflow | coding | writing | research | roleplay | business | learningrequired:

一级固定分类筛选

pageintegerrequired:

页码,从 1 开始,默认 1

limitintegerrequired:

每页数量,默认 24,最大 100

sortByrecent | namerequired:

recent 按更新时间倒序,name 按名称升序

response example
{
  "success": true,
  "data": [
    {
      "id": "cm0soul123xyz",
      "slug": "writing-mentor",
      "name": "Writing Mentor",
      "description": "A concise editorial SOUL for structured long-form writing.",
      "category": "writing",
      "status": "APPROVED",
      "filePath": "souls/writing-mentor/SOUL.md",
      "frontmatter": {
        "name": "Writing Mentor",
        "description": "A concise editorial SOUL for structured long-form writing.",
        "category": "writing"
      },
      "frontmatterRaw": "name: Writing Mentor\ndescription: A concise editorial SOUL for structured long-form writing.\ncategory: writing",
      "downloads": 32,
      "createdAt": "2026-03-01T09:30:00.000Z",
      "updatedAt": "2026-03-08T14:12:00.000Z",
      "authorId": "user_123"
    }
  ],
  "page": 1,
  "limit": 24,
  "total": 18,
  "totalPages": 1,
  "filters": {
    "q": "mentor",
    "category": "writing",
    "sortBy": "recent"
  }
}
curl example
curl -X GET "https://your-domain.com/api/v1/souls/search?category=writing&q=mentor&sortBy=recent" \
  -H "Authorization: Bearer sk_live_your_api_key"
GET /api/v1/souls/{slug}single item

按 slug 获取单个 SOUL 元数据。已审批 SOUL 公开可读;未审批条目仅作者或管理员在站内认证上下文可访问。

curl example
curl -X GET "https://your-domain.com/api/v1/souls/writing-mentor"
response example
{
  "success": true,
  "data": {
    "id": "cm0soul123xyz",
    "slug": "writing-mentor",
    "name": "Writing Mentor",
    "description": "A concise editorial SOUL for structured long-form writing.",
    "category": "writing",
    "status": "APPROVED",
    "filePath": "souls/writing-mentor/SOUL.md",
    "frontmatter": {
      "name": "Writing Mentor",
      "description": "A concise editorial SOUL for structured long-form writing.",
      "category": "writing"
    },
    "frontmatterRaw": "name: Writing Mentor\ndescription: A concise editorial SOUL for structured long-form writing.\ncategory: writing",
    "downloads": 32,
    "createdAt": "2026-03-01T09:30:00.000Z",
    "updatedAt": "2026-03-08T14:12:00.000Z",
    "authorId": "user_123"
  }
}
GET /api/v1/souls/categoriesfixed taxonomy

返回 SOUL 市场使用的固定一级分类列表,适合前端下拉框、筛选器和客户端校验直接复用。

curl example
curl -X GET "https://your-domain.com/api/v1/souls/categories"
response example
{
  "success": true,
  "data": [
    { "value": "assistant", "label": "助手" },
    { "value": "workflow", "label": "工作流" },
    { "value": "coding", "label": "编程" },
    { "value": "writing", "label": "写作" },
    { "value": "research", "label": "研究" },
    { "value": "roleplay", "label": "角色" },
    { "value": "business", "label": "商业" },
    { "value": "learning", "label": "学习" }
  ]
}
GET /api/v1/souls/{slug}/downloadraw markdown

按 slug 下载公开 SOUL 的原始 `SOUL.md` 内容。返回 `text/markdown`,适合自动拉取到本地工作流或 Agent 配置管线。有效 API Key 请求会消耗 SOUL/DOWNLOAD 日额度。

curl example
curl -L "https://your-domain.com/api/v1/souls/writing-mentor/download" \
  -H "Authorization: Bearer sk_live_your_api_key" \
  -o SOUL.md

如果 slug 不存在或 SOUL 未公开,接口返回 `404 Not found`。分钟限流或日配额用尽时返回 `429`,并携带 `X-RateLimit-*` 响应头。

errors.tsstandard envelope
MINUTE_RATE_LIMIT_EXCEEDEDHTTP 429

当前 IP 的分钟级请求额度已用尽

DAILY_QUOTA_EXCEEDEDHTTP 429

当前 API Key 超过 UTC 日配额

error example
{
  "success": false,
  "error": {
    "code": "DAILY_QUOTA_EXCEEDED",
    "message": "Daily API quota exceeded"
  }
}
openapi.jsonmachine-readable spec

OpenAPI 3.1 文档

推荐先直接打开 JSON 导入 Postman、Insomnia 或 SDK 生成器。只有在需要人工检查字段时再展开完整内容。

打开 JSON
展开完整 OpenAPI JSON
{
  "openapi": "3.1.0",
  "info": {
    "title": "Skills + SOUL Marketplace API",
    "version": "1.2.0",
    "description": "REST API for browsing approved agent skills and SOUL markdown assets with anonymous access, IP-based minute limits, and optional API key daily quotas on metered public endpoints."
  },
  "servers": [
    {
      "url": "/",
      "description": "Current deployment"
    }
  ],
  "tags": [
    {
      "name": "Skills",
      "description": "Public skill discovery endpoints"
    },
    {
      "name": "Souls",
      "description": "Public SOUL discovery and download endpoints"
    }
  ],
  "components": {
    "securitySchemes": {
      "BearerApiKey": {
        "type": "http",
        "scheme": "bearer",
        "bearerFormat": "API Key",
        "description": "Optional API key from the dashboard, e.g. Bearer sk_live_xxx. Valid API keys raise the per-minute IP limit and still enforce daily quota."
      }
    },
    "schemas": {
      "Skill": {
        "type": "object",
        "required": [
          "id",
          "slug",
          "name",
          "description",
          "tags",
          "status",
          "filePath",
          "downloads",
          "createdAt",
          "updatedAt",
          "authorId"
        ],
        "properties": {
          "id": {
            "type": "string",
            "example": "cm0abc123xyz"
          },
          "slug": {
            "type": "string",
            "example": "csv-pipeline"
          },
          "name": {
            "type": "string",
            "example": "CSV Pipeline"
          },
          "description": {
            "type": "string",
            "example": "Process, transform, and analyze CSV datasets."
          },
          "category": {
            "type": [
              "string",
              "null"
            ],
            "example": "data"
          },
          "tags": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "example": [
              "csv",
              "etl",
              "analysis"
            ]
          },
          "status": {
            "type": "string",
            "enum": [
              "PENDING",
              "APPROVED",
              "REJECTED"
            ],
            "example": "APPROVED"
          },
          "filePath": {
            "type": "string",
            "example": "skills/csv-pipeline/SKILL.md"
          },
          "frontmatter": {
            "description": "Raw frontmatter JSON when available.",
            "nullable": true
          },
          "frontmatterRaw": {
            "type": [
              "string",
              "null"
            ],
            "example": "name: CSV Pipeline"
          },
          "downloads": {
            "type": "integer",
            "example": 128
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "updatedAt": {
            "type": "string",
            "format": "date-time"
          },
          "authorId": {
            "type": "string",
            "example": "user_123"
          }
        }
      },
      "Soul": {
        "type": "object",
        "required": [
          "id",
          "slug",
          "name",
          "description",
          "category",
          "status",
          "filePath",
          "downloads",
          "createdAt",
          "updatedAt",
          "authorId"
        ],
        "properties": {
          "id": {
            "type": "string",
            "example": "cm0soul123xyz"
          },
          "slug": {
            "type": "string",
            "example": "writing-mentor"
          },
          "name": {
            "type": "string",
            "example": "Writing Mentor"
          },
          "description": {
            "type": "string",
            "example": "A concise editorial SOUL for structured long-form writing."
          },
          "category": {
            "type": "string",
            "enum": [
              "assistant",
              "workflow",
              "coding",
              "writing",
              "research",
              "roleplay",
              "business",
              "learning"
            ],
            "example": "writing"
          },
          "status": {
            "type": "string",
            "enum": [
              "PENDING",
              "APPROVED",
              "REJECTED"
            ],
            "example": "APPROVED"
          },
          "filePath": {
            "type": "string",
            "example": "souls/writing-mentor/SOUL.md"
          },
          "frontmatter": {
            "description": "Raw frontmatter JSON when available.",
            "nullable": true
          },
          "frontmatterRaw": {
            "type": [
              "string",
              "null"
            ],
            "example": "name: Writing Mentor\ndescription: Editorial SOUL\ncategory: writing"
          },
          "downloads": {
            "type": "integer",
            "example": 32
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "updatedAt": {
            "type": "string",
            "format": "date-time"
          },
          "authorId": {
            "type": "string",
            "example": "user_123"
          }
        }
      },
      "SkillSearchResponse": {
        "type": "object",
        "required": [
          "success",
          "data",
          "page",
          "limit",
          "total",
          "totalPages",
          "filters"
        ],
        "properties": {
          "success": {
            "type": "boolean",
            "example": true
          },
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Skill"
            }
          },
          "page": {
            "type": "integer",
            "example": 1
          },
          "limit": {
            "type": "integer",
            "example": 24
          },
          "total": {
            "type": "integer",
            "example": 42
          },
          "totalPages": {
            "type": "integer",
            "example": 2
          },
          "filters": {
            "type": "object",
            "properties": {
              "q": {
                "type": [
                  "string",
                  "null"
                ],
                "example": "csv"
              },
              "category": {
                "type": [
                  "string",
                  "null"
                ],
                "example": "data"
              },
              "tag": {
                "type": [
                  "string",
                  "null"
                ],
                "example": "analysis"
              },
              "sortBy": {
                "type": "string",
                "enum": [
                  "popular",
                  "recent"
                ],
                "example": "popular"
              }
            }
          },
          "note": {
            "type": "string",
            "nullable": true
          }
        }
      },
      "SoulSearchResponse": {
        "type": "object",
        "required": [
          "success",
          "data",
          "page",
          "limit",
          "total",
          "totalPages",
          "filters"
        ],
        "properties": {
          "success": {
            "type": "boolean",
            "example": true
          },
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Soul"
            }
          },
          "page": {
            "type": "integer",
            "example": 1
          },
          "limit": {
            "type": "integer",
            "example": 24
          },
          "total": {
            "type": "integer",
            "example": 18
          },
          "totalPages": {
            "type": "integer",
            "example": 1
          },
          "filters": {
            "type": "object",
            "properties": {
              "q": {
                "type": [
                  "string",
                  "null"
                ],
                "example": "mentor"
              },
              "category": {
                "type": [
                  "string",
                  "null"
                ],
                "example": "writing"
              },
              "sortBy": {
                "type": "string",
                "enum": [
                  "recent",
                  "name"
                ],
                "example": "recent"
              }
            }
          }
        }
      },
      "SoulDetailResponse": {
        "type": "object",
        "required": [
          "success",
          "data"
        ],
        "properties": {
          "success": {
            "type": "boolean",
            "example": true
          },
          "data": {
            "$ref": "#/components/schemas/Soul"
          }
        }
      },
      "SoulCategoriesResponse": {
        "type": "object",
        "required": [
          "success",
          "data"
        ],
        "properties": {
          "success": {
            "type": "boolean",
            "example": true
          },
          "data": {
            "type": "array",
            "items": {
              "type": "object",
              "required": [
                "value",
                "label"
              ],
              "properties": {
                "value": {
                  "type": "string",
                  "enum": [
                    "assistant",
                    "workflow",
                    "coding",
                    "writing",
                    "research",
                    "roleplay",
                    "business",
                    "learning"
                  ],
                  "example": "writing"
                },
                "label": {
                  "type": "string",
                  "example": "写作"
                }
              }
            }
          }
        }
      },
      "ErrorResponse": {
        "type": "object",
        "required": [
          "success",
          "error"
        ],
        "properties": {
          "success": {
            "type": "boolean",
            "example": false
          },
          "error": {
            "type": "object",
            "required": [
              "code",
              "message"
            ],
            "properties": {
              "code": {
                "type": "string",
                "enum": [
                  "MINUTE_RATE_LIMIT_EXCEEDED",
                  "DAILY_QUOTA_EXCEEDED"
                ],
                "example": "MINUTE_RATE_LIMIT_EXCEEDED"
              },
              "message": {
                "type": "string",
                "example": "Per-minute IP rate limit exceeded"
              }
            }
          }
        }
      }
    },
    "parameters": {
      "Query": {
        "name": "q",
        "in": "query",
        "required": false,
        "schema": {
          "type": "string"
        },
        "description": "Optional keyword query. Matches name, description, category, and tags when supported by the endpoint."
      },
      "Category": {
        "name": "category",
        "in": "query",
        "required": false,
        "schema": {
          "type": "string"
        },
        "description": "Optional category filter."
      },
      "Tag": {
        "name": "tag",
        "in": "query",
        "required": false,
        "schema": {
          "type": "string"
        },
        "description": "Optional exact tag filter for skills."
      },
      "Page": {
        "name": "page",
        "in": "query",
        "required": false,
        "schema": {
          "type": "integer",
          "minimum": 1,
          "default": 1
        },
        "description": "1-based page number."
      },
      "Limit": {
        "name": "limit",
        "in": "query",
        "required": false,
        "schema": {
          "type": "integer",
          "minimum": 1,
          "maximum": 100,
          "default": 24
        },
        "description": "Page size. Values above 100 are clamped to 100."
      },
      "SkillSortBy": {
        "name": "sortBy",
        "in": "query",
        "required": false,
        "schema": {
          "type": "string",
          "enum": [
            "recent",
            "popular"
          ]
        },
        "description": "Skill sort order. `recent` sorts by update time descending. Any other value falls back to popularity."
      },
      "SoulSortBy": {
        "name": "sortBy",
        "in": "query",
        "required": false,
        "schema": {
          "type": "string",
          "enum": [
            "recent",
            "name"
          ]
        },
        "description": "SOUL sort order. `recent` sorts by update time descending. `name` sorts alphabetically."
      },
      "Slug": {
        "name": "slug",
        "in": "path",
        "required": true,
        "schema": {
          "type": "string"
        },
        "description": "Unique public slug."
      }
    },
    "responses": {
      "QuotaExceeded": {
        "description": "Request rejected because the IP minute limit or API key daily quota is exhausted.",
        "headers": {
          "X-RateLimit-Tier": {
            "schema": {
              "type": "string"
            },
            "description": "Applied minute-limit tier: `anonymous` or `api-key`."
          },
          "X-RateLimit-Minute-Limit": {
            "schema": {
              "type": "string"
            },
            "description": "Per-minute request limit for the resolved IP tier."
          },
          "X-RateLimit-Minute-Remaining": {
            "schema": {
              "type": "string"
            },
            "description": "Remaining requests in the current minute window for the resolved IP tier."
          },
          "X-RateLimit-Minute-Reset": {
            "schema": {
              "type": "string"
            },
            "description": "Unix timestamp in seconds when the current minute window resets."
          },
          "X-RateLimit-Daily-Limit": {
            "schema": {
              "type": "string"
            },
            "description": "Configured daily quota limit for a valid API key request."
          },
          "X-RateLimit-Daily-Remaining": {
            "schema": {
              "type": "string"
            },
            "description": "Remaining successful requests for the current UTC day on a valid API key request."
          }
        },
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ErrorResponse"
            }
          }
        }
      },
      "NotFoundText": {
        "description": "Resource not found or not publicly accessible.",
        "content": {
          "text/plain": {
            "schema": {
              "type": "string",
              "example": "Not found"
            }
          }
        }
      }
    }
  },
  "paths": {
    "/api/v1/skills/search": {
      "get": {
        "tags": [
          "Skills"
        ],
        "summary": "Browse approved skills",
        "description": "Browse approved skills with optional keyword, category, tag, paging, and sorting parameters. Anonymous requests are allowed and limited to 25 requests per minute per IP. Valid API keys raise the same IP bucket to 35 requests per minute and still enforce daily quota. Invalid API keys are treated as anonymous requests.",
        "security": [
          {},
          {
            "BearerApiKey": []
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/Query"
          },
          {
            "$ref": "#/components/parameters/Category"
          },
          {
            "$ref": "#/components/parameters/Tag"
          },
          {
            "$ref": "#/components/parameters/Page"
          },
          {
            "$ref": "#/components/parameters/Limit"
          },
          {
            "$ref": "#/components/parameters/SkillSortBy"
          }
        ],
        "responses": {
          "200": {
            "description": "Search result page.",
            "headers": {
              "X-RateLimit-Tier": {
                "schema": {
                  "type": "string"
                },
                "description": "Applied minute-limit tier: `anonymous` or `api-key`."
              },
              "X-RateLimit-Minute-Limit": {
                "schema": {
                  "type": "string"
                },
                "description": "Per-minute request limit for the resolved IP tier."
              },
              "X-RateLimit-Minute-Remaining": {
                "schema": {
                  "type": "string"
                },
                "description": "Remaining requests in the current minute window for the resolved IP tier."
              },
              "X-RateLimit-Minute-Reset": {
                "schema": {
                  "type": "string"
                },
                "description": "Unix timestamp in seconds when the current minute window resets."
              },
              "X-RateLimit-Daily-Limit": {
                "schema": {
                  "type": "string"
                },
                "description": "Configured daily quota limit for a valid API key request."
              },
              "X-RateLimit-Daily-Remaining": {
                "schema": {
                  "type": "string"
                },
                "description": "Remaining successful requests for the current UTC day on a valid API key request."
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SkillSearchResponse"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/QuotaExceeded"
          }
        }
      }
    },
    "/api/v1/skills/ai-search": {
      "get": {
        "tags": [
          "Skills"
        ],
        "summary": "AI-assisted skill search",
        "description": "Experimental alias for search. Anonymous requests are allowed and limited to 25 requests per minute per IP. Valid API keys raise the same IP bucket to 35 requests per minute and still enforce daily quota. Invalid API keys are treated as anonymous requests.",
        "security": [
          {},
          {
            "BearerApiKey": []
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/Query"
          },
          {
            "$ref": "#/components/parameters/Category"
          },
          {
            "$ref": "#/components/parameters/Tag"
          },
          {
            "$ref": "#/components/parameters/Page"
          },
          {
            "$ref": "#/components/parameters/Limit"
          },
          {
            "$ref": "#/components/parameters/SkillSortBy"
          }
        ],
        "responses": {
          "200": {
            "description": "AI search result page.",
            "headers": {
              "X-RateLimit-Tier": {
                "schema": {
                  "type": "string"
                },
                "description": "Applied minute-limit tier: `anonymous` or `api-key`."
              },
              "X-RateLimit-Minute-Limit": {
                "schema": {
                  "type": "string"
                },
                "description": "Per-minute request limit for the resolved IP tier."
              },
              "X-RateLimit-Minute-Remaining": {
                "schema": {
                  "type": "string"
                },
                "description": "Remaining requests in the current minute window for the resolved IP tier."
              },
              "X-RateLimit-Minute-Reset": {
                "schema": {
                  "type": "string"
                },
                "description": "Unix timestamp in seconds when the current minute window resets."
              },
              "X-RateLimit-Daily-Limit": {
                "schema": {
                  "type": "string"
                },
                "description": "Configured daily quota limit for a valid API key request."
              },
              "X-RateLimit-Daily-Remaining": {
                "schema": {
                  "type": "string"
                },
                "description": "Remaining successful requests for the current UTC day on a valid API key request."
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SkillSearchResponse"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/QuotaExceeded"
          }
        }
      }
    },
    "/api/v1/souls/search": {
      "get": {
        "tags": [
          "Souls"
        ],
        "summary": "Browse approved SOUL assets",
        "description": "Browse approved SOUL markdown assets with optional keyword, category, paging, and sorting parameters. Anonymous requests are allowed and limited to 25 requests per minute per IP. Valid API keys raise the same IP bucket to 35 requests per minute and still enforce daily quota. Invalid API keys are treated as anonymous requests.",
        "security": [
          {},
          {
            "BearerApiKey": []
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/Query"
          },
          {
            "$ref": "#/components/parameters/Category"
          },
          {
            "$ref": "#/components/parameters/Page"
          },
          {
            "$ref": "#/components/parameters/Limit"
          },
          {
            "$ref": "#/components/parameters/SoulSortBy"
          }
        ],
        "responses": {
          "200": {
            "description": "SOUL search result page.",
            "headers": {
              "X-RateLimit-Tier": {
                "schema": {
                  "type": "string"
                },
                "description": "Applied minute-limit tier: `anonymous` or `api-key`."
              },
              "X-RateLimit-Minute-Limit": {
                "schema": {
                  "type": "string"
                },
                "description": "Per-minute request limit for the resolved IP tier."
              },
              "X-RateLimit-Minute-Remaining": {
                "schema": {
                  "type": "string"
                },
                "description": "Remaining requests in the current minute window for the resolved IP tier."
              },
              "X-RateLimit-Minute-Reset": {
                "schema": {
                  "type": "string"
                },
                "description": "Unix timestamp in seconds when the current minute window resets."
              },
              "X-RateLimit-Daily-Limit": {
                "schema": {
                  "type": "string"
                },
                "description": "Configured daily quota limit for a valid API key request."
              },
              "X-RateLimit-Daily-Remaining": {
                "schema": {
                  "type": "string"
                },
                "description": "Remaining successful requests for the current UTC day on a valid API key request."
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SoulSearchResponse"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/QuotaExceeded"
          }
        }
      }
    },
    "/api/v1/souls/categories": {
      "get": {
        "tags": [
          "Souls"
        ],
        "summary": "List SOUL categories",
        "description": "Return the fixed top-level category list used by the SOUL marketplace.",
        "responses": {
          "200": {
            "description": "SOUL category list.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SoulCategoriesResponse"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/souls/{slug}": {
      "get": {
        "tags": [
          "Souls"
        ],
        "summary": "Get a SOUL by slug",
        "description": "Return a single SOUL record by slug. Approved SOUL entries are public. Unapproved SOUL entries return 404 unless accessed through the authenticated site owner/admin flow.",
        "parameters": [
          {
            "$ref": "#/components/parameters/Slug"
          }
        ],
        "responses": {
          "200": {
            "description": "SOUL detail.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SoulDetailResponse"
                }
              }
            }
          },
          "404": {
            "description": "Resource not found or not publicly accessible.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "success",
                    "error"
                  ],
                  "properties": {
                    "success": {
                      "type": "boolean",
                      "example": false
                    },
                    "error": {
                      "type": "object",
                      "required": [
                        "code",
                        "message"
                      ],
                      "properties": {
                        "code": {
                          "type": "string",
                          "example": "NOT_FOUND"
                        },
                        "message": {
                          "type": "string",
                          "example": "Not found"
                        }
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/souls/{slug}/download": {
      "get": {
        "tags": [
          "Souls"
        ],
        "summary": "Download an approved SOUL markdown file",
        "description": "Download the raw `SOUL.md` content for a public SOUL by slug. Anonymous requests are allowed and limited to 25 requests per minute per IP. Valid API keys raise the same IP bucket to 35 requests per minute and consume `SOUL/DOWNLOAD` daily quota. Unapproved SOUL entries return 404 unless accessed through the authenticated site owner/admin flow.",
        "security": [
          {},
          {
            "BearerApiKey": []
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/Slug"
          }
        ],
        "responses": {
          "200": {
            "description": "Raw markdown file.",
            "headers": {
              "X-RateLimit-Tier": {
                "schema": {
                  "type": "string"
                },
                "description": "Applied minute-limit tier: `anonymous` or `api-key`."
              },
              "X-RateLimit-Minute-Limit": {
                "schema": {
                  "type": "string"
                },
                "description": "Per-minute request limit for the resolved IP tier."
              },
              "X-RateLimit-Minute-Remaining": {
                "schema": {
                  "type": "string"
                },
                "description": "Remaining requests in the current minute window for the resolved IP tier."
              },
              "X-RateLimit-Minute-Reset": {
                "schema": {
                  "type": "string"
                },
                "description": "Unix timestamp in seconds when the current minute window resets."
              },
              "X-RateLimit-Daily-Limit": {
                "schema": {
                  "type": "string"
                },
                "description": "Configured daily quota limit for a valid API key request."
              },
              "X-RateLimit-Daily-Remaining": {
                "schema": {
                  "type": "string"
                },
                "description": "Remaining successful requests for the current UTC day on a valid API key request."
              },
              "Content-Disposition": {
                "schema": {
                  "type": "string"
                },
                "description": "Attachment filename, e.g. `writing-mentor.md`."
              }
            },
            "content": {
              "text/markdown": {
                "schema": {
                  "type": "string",
                  "example": "---\nname: Writing Mentor\ndescription: Editorial SOUL\ncategory: writing\n---\n\nYou are a concise editorial mentor."
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFoundText"
          },
          "429": {
            "$ref": "#/components/responses/QuotaExceeded"
          }
        }
      }
    }
  }
}