---
name: "openapi-rest-explorer"
description: "Browse API endpoints interactively. Pre-loaded with the SkillSlap REST API spec — endpoints, parameters, response schemas, and status codes all in one view."
metadata:
  version: "1.0.0"
  runtime:
    entrypoint: "openapi-explorer.html"
---

# OpenAPI REST Explorer

> **Purpose:** Render any REST API spec as an interactive, searchable reference UI. Pre-loaded with the SkillSlap API endpoints. Click an endpoint in the sidebar to see its full documentation: description, parameters (with types and required flags), example response schema, and status codes.

---

## What's Included (Pre-loaded)

| Method | Path | Description |
|--------|------|-------------|
| GET | /skills | List all skills with search and filtering |
| GET | /skills/{id} | Retrieve full skill details |
| POST | /skills | Create and publish a new skill |
| POST | /skills/{id}/fork | Fork a skill to your account |
| GET | /skills/{id}/verifications | List verification runs |

---

## How to Use

1. Click any endpoint in the left sidebar
2. Read the description, parameters, and response schema
3. Use **Try It Out** to manually test against a live API

---

## Reading Parameters

- **Red asterisk (*)** — required parameter
- `in: path` — part of the URL
- `in: query` — appended as `?key=value`
- `in: body` — sent as JSON request body
- `in: header` — sent as HTTP header

---

## Adapting to Your API

Modify the `api.endpoints` array in the source to document your own API. The renderer handles GET, POST, PUT, DELETE, and PATCH with color-coded method badges.


---

## Files

### `openapi-explorer.html`

```html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>OpenAPI REST Explorer</title>
<style>
* { box-sizing: border-box; margin: 0; padding: 0; }
body { background: #0a0a14; color: #e2e8f0; font-family: system-ui, sans-serif; display: flex; flex-direction: column; height: 100vh; }
header { background: #1a1a2e; border-bottom: 1px solid #2d2d4e; padding: 12px 20px; flex-shrink: 0; }
.h-title { font-size: 16px; color: #a78bfa; font-weight: 700; }
.h-sub { font-size: 11px; color: #4b5563; margin-top: 3px; }
.layout { display: flex; flex: 1; overflow: hidden; }
.sidebar { width: 258px; background: #0d0d1c; border-right: 1px solid #1e1e3a; overflow-y: auto; padding: 14px; flex-shrink: 0; }
.sb-label { font-size: 10px; text-transform: uppercase; letter-spacing: 1px; color: #4b5563; margin-bottom: 10px; }
.ep { display: flex; align-items: center; gap: 8px; padding: 8px 10px; border-radius: 6px; cursor: pointer; margin-bottom: 3px; transition: background 0.1s; }
.ep:hover { background: #1a1a2e; }
.ep.sel { background: #2d1b69; }
.badge { font-size: 10px; font-weight: 800; padding: 2px 7px; border-radius: 4px; min-width: 40px; text-align: center; }
.GET    { background: #064e3b; color: #6ee7b7; }
.POST   { background: #1e3a5f; color: #93c5fd; }
.PUT    { background: #451a03; color: #fcd34d; }
.DELETE { background: #450a0a; color: #fca5a5; }
.PATCH  { background: #3b1f6e; color: #c4b5fd; }
.ep-path { font-family: monospace; font-size: 11px; color: #94a3b8; flex: 1; }
.main { flex: 1; overflow-y: auto; padding: 24px 28px; }
.ep-title { font-size: 20px; font-weight: 700; display: flex; align-items: center; gap: 12px; margin-bottom: 10px; }
.ep-desc { color: #94a3b8; font-size: 13px; line-height: 1.7; margin-bottom: 24px; }
.section { margin-bottom: 22px; }
.sec-hd { font-size: 10px; text-transform: uppercase; letter-spacing: 1px; color: #4b5563; border-bottom: 1px solid #1e1e3a; padding-bottom: 7px; margin-bottom: 12px; }
.param { display: flex; gap: 12px; padding: 8px 0; border-bottom: 1px solid #12121e; align-items: flex-start; }
.pname { font-family: monospace; font-size: 12px; color: #c4b5fd; min-width: 130px; }
.req { color: #f87171; margin-left: 1px; }
.pin { font-size: 10px; color: #4b5563; margin-top: 2px; }
.ptype { font-size: 10px; background: #1e1e3a; padding: 2px 7px; border-radius: 4px; color: #6b7280; white-space: nowrap; }
.pdesc { font-size: 12px; color: #6b7280; flex: 1; }
.codeblock { background: #0a0a14; border: 1px solid #1e1e3a; border-radius: 8px; padding: 16px; font-family: monospace; font-size: 12px; line-height: 1.65; color: #6ee7b7; overflow-x: auto; white-space: pre; }
.sc { display: inline-block; font-family: monospace; font-size: 11px; padding: 2px 9px; border-radius: 4px; margin-right: 6px; font-weight: 700; }
.s2 { background: #064e3b; color: #6ee7b7; }
.s4 { background: #451a03; color: #fcd34d; }
.s401 { background: #450a0a; color: #fca5a5; }
.trybtn { margin-top: 18px; background: #6d28d9; color: white; border: none; padding: 9px 18px; border-radius: 7px; cursor: pointer; font-size: 13px; font-weight: 700; }
.trybtn:hover { background: #7c3aed; }
.empty { text-align: center; padding-top: 80px; color: #2d2d4e; }
.empty-icon { font-size: 48px; margin-bottom: 14px; }
</style>
</head>
<body>
<header>
  <div class="h-title">SkillSlap API <span style="font-size:11px;font-weight:400;background:#2d1b69;color:#a78bfa;padding:2px 9px;border-radius:12px;margin-left:8px">v1.0</span></div>
  <div class="h-sub">REST API &#8226; Base: https://skillslap.com/api/v1 &#8226; Auth: Bearer token</div>
</header>
<div class="layout">
  <div class="sidebar">
    <div class="sb-label">Skills Endpoints</div>
    <div id="sb"></div>
  </div>
  <div class="main" id="main">
    <div class="empty"><div class="empty-icon">&#9889;</div><div style="color:#4b5563;font-size:15px">Select an endpoint</div></div>
  </div>
</div>
<script>
var sel = -1;
var eps = [
  { method:'GET', path:'/skills', sum:'List Skills',
    desc:'Returns a paginated list of published skills. Supports full-text search, tag filtering, verified-only toggle, and cursor-based pagination.',
    params:[
      {n:'q',in:'query',t:'string',req:false,d:'Full-text search query'},
      {n:'tags',in:'query',t:'string[]',req:false,d:'Comma-separated tag filter'},
      {n:'verified',in:'query',t:'boolean',req:false,d:'Only return AI-verified skills'},
      {n:'page',in:'query',t:'integer',req:false,d:'Page number (default: 1)'},
      {n:'limit',in:'query',t:'integer',req:false,d:'Items per page, max 100 (default: 20)'},
    ],
    res:'{\n  "skills": [\n    {\n      "id": "ck8x9q1a0000001",\n      "title": "Code Reviewer",\n      "description": "Systematic code review agent",\n      "author": { "username": "atapifire" },\n      "tags": ["code-quality", "review"],\n      "overall_score": 0.92,\n      "fork_count": 14,\n      "verified": true\n    }\n  ],\n  "total": 42,\n  "page": 1\n}',
    codes:['200','400'] },
  { method:'GET', path:'/skills/{id}', sum:'Get Skill',
    desc:'Retrieve full skill details. Use X-Detail-Level header to control response depth: 1 = metadata only, 2 = metadata + content, 3 = full detail including files and verification results.',
    params:[
      {n:'id',in:'path',t:'string',req:true,d:'Skill ID or slug'},
      {n:'X-Detail-Level',in:'header',t:'1 | 2 | 3',req:false,d:'Response detail level (default: 2)'},
    ],
    res:'{\n  "id": "ck8x9q1a0000001",\n  "title": "Code Reviewer",\n  "content": "# Code Reviewer\\n\\n> **Purpose:** ...",\n  "version": "1.0.0",\n  "invocation_type": "agent",\n  "overall_score": 0.92,\n  "verification": {\n    "status": "verified",\n    "last_verified_at": "2026-02-20T10:00:00Z"\n  }\n}',
    codes:['200','401','404'] },
  { method:'POST', path:'/skills', sum:'Create Skill',
    desc:'Publish a new skill to the marketplace. Skills start in draft status and must pass AI verification before appearing in search results and the public marketplace.',
    params:[
      {n:'title',in:'body',t:'string',req:true,d:'Skill title, max 100 characters'},
      {n:'description',in:'body',t:'string',req:true,d:'Short description, max 500 characters'},
      {n:'content',in:'body',t:'string',req:true,d:'Full skill content in Markdown'},
      {n:'tags',in:'body',t:'string[]',req:false,d:'Up to 10 tags'},
      {n:'invocation_type',in:'body',t:'agent|user|tool|context',req:true,d:'How this skill is invoked'},
    ],
    res:'{\n  "id": "ck9abc123",\n  "title": "My New Skill",\n  "status": "draft",\n  "created_at": "2026-02-20T10:00:00Z"\n}',
    codes:['201','400','401'] },
  { method:'POST', path:'/skills/{id}/fork', sum:'Fork Skill',
    desc:'Create a personal copy of any skill. The fork maintains attribution to the original author and increments the original skill fork count. Forked skills start as drafts.',
    params:[
      {n:'id',in:'path',t:'string',req:true,d:'ID of the skill to fork'},
      {n:'title',in:'body',t:'string',req:false,d:'Optional custom title for your fork'},
    ],
    res:'{\n  "id": "ck9fork456",\n  "title": "Code Reviewer (fork)",\n  "forked_from": "ck8x9q1a0000001",\n  "status": "draft"\n}',
    codes:['201','401','404'] },
  { method:'GET', path:'/skills/{id}/verifications', sum:'Get Verifications',
    desc:'List all AI verification runs for a skill. Each verification includes per-category scores, execution trace, screenshot URLs, and an overall pass/fail decision.',
    params:[
      {n:'id',in:'path',t:'string',req:true,d:'Skill ID'},
    ],
    res:'{\n  "verifications": [\n    {\n      "id": "ver_abc",\n      "status": "passed",\n      "overall_score": 0.92,\n      "categories": {\n        "correctness": 0.95,\n        "completeness": 0.88,\n        "format_quality": 0.92\n      },\n      "created_at": "2026-02-20T10:00:00Z"\n    }\n  ]\n}',
    codes:['200','404'] },
];

function renderSb() {
  document.getElementById('sb').innerHTML = eps.map(function(e,i){
    return '<div class="ep'+(i===sel?' sel':'')+'" onclick="pick('+i+')">'
      +'<span class="badge '+e.method+'">'+e.method+'</span>'
      +'<span class="ep-path">'+e.path+'</span></div>';
  }).join('');
}

function scClass(c) {
  if (c==='200'||c==='201') return 's2';
  if (c==='401') return 's401';
  return 's4';
}

function pick(i) {
  sel = i;
  renderSb();
  var e = eps[i];
  var params = e.params.map(function(p){
    return '<div class="param">'
      +'<div><div class="pname">'+p.n+(p.req?'<span class="req">*</span>':'')+'</div>'
      +'<div class="pin">in: '+p.in+'</div></div>'
      +'<span class="ptype">'+p.t+'</span>'
      +'<span class="pdesc">'+p.d+'</span></div>';
  }).join('');
  var codes = e.codes.map(function(c){
    return '<span class="sc '+scClass(c)+'">'+c+'</span>';
  }).join('');
  document.getElementById('main').innerHTML =
    '<div class="ep-title"><span class="badge '+e.method+'" style="font-size:13px">'+e.method+'</span>'+e.path+'</div>'
    +'<div class="ep-desc">'+e.desc+'</div>'
    +(params?'<div class="section"><div class="sec-hd">Parameters</div>'+params+'</div>':'')
    +'<div class="section"><div class="sec-hd">Response</div><div class="codeblock">'+e.res+'</div></div>'
    +'<div class="section"><div class="sec-hd">Status Codes</div>'+codes+'</div>'
    +'<button class="trybtn">Try It Out &rarr;</button>';
}

renderSb();
pick(0);
</script>
</body>
</html>
```
