REST API reference
Complete reference for all mog.md REST API endpoints.
The mog REST API is available at https://api.mog.md. All endpoints are under /v1/. Requests and responses are JSON unless otherwise noted.
Base URL
https://api.mog.md
Authentication
Protected endpoints require a Bearer token in the Authorization header:
Authorization: Bearer <token>
Tokens are issued via the device code flow or retrieved from your dashboard. Different endpoints require different scopes:
| Scope | Required for |
|---|---|
read | Entitlements, reviews |
purchase | Purchases |
download | Download URLs |
sell | Vendor operations, uploads |
Errors
Errors return a JSON body with a message field:
{ "message": "Listing not found" }
| Status | Meaning |
|---|---|
400 | Bad request / validation error |
401 | Missing or invalid token |
402 | Payment / approval required |
403 | Insufficient token scope |
404 | Resource not found |
422 | Unprocessable entity |
429 | Rate limit exceeded |
500 | Internal server error |
Health
GET /health
Returns API health status including database and Redis connectivity. No authentication required.
// All services healthy — HTTP 200
{
"ok": true,
"version": "0.1.0",
"status": "healthy",
"db": "ok",
"redis": "ok"
}
// Degraded — HTTP 503
{
"ok": false,
"version": "0.1.0",
"status": "degraded",
"db": "error",
"redis": "ok"
}
When status is "degraded", the HTTP status code is 503. Use this endpoint for liveness/readiness probes in your deployment platform.
Authentication endpoints
Start device flow
POST /v1/auth/device/start — Rate limit: 10/min per IP
Initiates a device code flow.
Response:
{
"deviceCode": "internal-device-code",
"userCode": "XKCD-7Z4B",
"verificationUri": "https://mog.md/device",
"expiresIn": 900,
"interval": 5
}
Poll device flow
POST /v1/auth/device/poll — Rate limit: 10/min per IP
Poll for approval. Call every interval seconds.
Request body:
{ "deviceCode": "internal-device-code" }
Response:
// Still waiting
{ "status": "authorization_pending" }
// Code expired
{ "status": "expired" }
// Approved — store this token
{
"status": "approved",
"token": "mog_...",
"tokenType": "Bearer",
"expiresIn": 31536000
}
Approve device code
POST /v1/auth/device/approve — Auth: Bearer token — Rate limit: 10/min per IP
Request body:
{ "userCode": "XKCD-7Z4B" }
Response:
{ "ok": true }
Revoke token
POST /v1/auth/token/revoke — Auth: Bearer token
{ "ok": true }
List API tokens
GET /v1/auth/tokens — Auth: Bearer token
Returns all active tokens for the current user.
{
"tokens": [
{
"id": "uuid",
"name": "CLI device token",
"scopes": ["read", "purchase", "download"],
"policyId": null,
"policyName": null,
"lastUsedAt": "2026-01-15T10:00:00.000Z",
"expiresAt": null,
"revokedAt": null,
"createdAt": "2026-01-01T00:00:00.000Z"
}
]
}
Revoke token by ID
DELETE /v1/auth/tokens/:id — Auth: Bearer token
{ "ok": true }
Search
GET /v1/search — Rate limit: 60/min per IP
Search packages. No authentication required.
Query parameters:
| Parameter | Type | Description |
|---|---|---|
q | string | Full-text search query |
type | skill | rule | bundle | template | Filter by package type |
target | cursor | claude-code | codex | generic | Filter by agent target |
sort | popular | recent | rated | price_asc | price_desc | Sort order (default: popular) |
page | number | Page number (default: 1) |
per_page | number | Results per page (1–50, default: 20) |
free | boolean | Only free packages |
Response:
{
"results": [
{
"id": "uuid",
"vendorSlug": "acme",
"slug": "router-eval",
"title": "Router Eval Skill",
"description": "...",
"type": "skill",
"targets": ["cursor", "claude-code"],
"priceCents": 0,
"currency": "usd",
"latestVersion": "1.0.0",
"installCount": 42,
"mogRating": "4.8",
"vendorName": "Acme Corp",
"vendorVerified": false
}
],
"total": 1,
"page": 1,
"perPage": 20
}
Listings
Get listing
GET /v1/listings/:vendor/:slug
Get a single listing by vendor slug and package slug. No authentication required.
Get releases
GET /v1/listings/:vendor/:slug/releases
{
"releases": [
{
"id": "uuid",
"version": "1.1.0",
"archiveSha256": "a3f8...",
"publishedAt": "2026-01-15T10:00:00.000Z"
}
]
}
Reviews
Get reviews
GET /v1/listings/:vendor/:slug/reviews
{
"reviews": [
{
"id": "uuid",
"userId": "uuid",
"rating": 5,
"body": "Excellent skill, saved me hours.",
"createdAt": "2026-01-10T08:00:00.000Z"
}
],
"rating": 4.8,
"ratingCount": 12
}
Create or update review
POST /v1/listings/:vendor/:slug/reviews — Auth: read scope
Request body:
{
"rating": 5, // required: 1–5
"body": "Great skill!" // optional: max 2000 chars
}
Purchases & entitlements
Purchase a listing
POST /v1/purchases — Auth: purchase scope — Rate limit: 20/min per user
Request body:
{
"listingId": "uuid", // required
"releaseId": "uuid", // optional: defaults to latest published release
"maxPriceCents": 1000 // optional: spend ceiling
}
Response (one of three shapes):
// Purchased
{
"status": "purchased",
"entitlementId": "uuid",
"orderId": "uuid",
"amountCents": 0
}
// Already owned
{ "status": "already_owned", "entitlementId": "uuid" }
// Approval required — HTTP 402
{
"status": "approval_required",
"approvalUrl": "https://mog.md/purchases/approve?listing=...",
"reason": "Price (1500¢) exceeds your policy limit (1000¢)"
}
List entitlements
GET /v1/entitlements — Auth: read scope
{
"entitlements": [
{
"id": "uuid",
"listingId": "uuid",
"releaseId": "uuid",
"vendorSlug": "acme",
"listingSlug": "router-eval",
"listingTitle": "Router Eval Skill",
"version": "1.0.0",
"grantedAt": "2026-01-15T10:00:00.000Z"
}
]
}
Check entitlement
GET /v1/entitlements/:listingId — Auth: read scope
// Owned — HTTP 200
{ "owned": true, "entitlement": { "...": "..." } }
// Not owned — HTTP 404
{ "owned": false }
List orders
GET /v1/orders — Auth: read scope
Returns the current user's order history.
{
"orders": [
{
"id": "uuid",
"listingId": "uuid",
"releaseId": "uuid",
"amountCents": 500,
"status": "paid",
"createdAt": "2026-01-15T10:00:00.000Z",
"vendorSlug": "acme",
"vendorName": "Acme Corp",
"listingSlug": "router-eval",
"listingTitle": "Router Eval Skill",
"version": "1.0.0"
}
]
}
Downloads
Get download URL
POST /v1/downloads — Auth: download scope
Request body:
{ "releaseId": "uuid" }
Response:
{
"url": "https://packages.mog.md/releases/uuid.zip?X-Amz-Expires=300&...",
"sha256": "a3f8c2d1...",
"expiresAt": "2026-01-15T10:05:00.000Z"
}
User
Get current user
GET /v1/users/me — Auth: Bearer token
{
"user": {
"id": "uuid",
"email": "user@example.com",
"name": "Ada Lovelace",
"avatarUrl": "https://...",
"emailVerified": true,
"createdAt": "2026-01-01T00:00:00.000Z"
}
}
Vendor
Create or update vendor profile
POST /v1/vendor/profile — Auth: sell scope
Request body:
{
"slug": "acme", // 2–32 chars, lowercase letters/numbers/hyphens
"displayName": "Acme Corp", // 1–64 chars
"bio": "We make skills.", // optional, max 500 chars
"website": "https://acme.io" // optional, must be valid URL
}
Start Stripe Connect onboarding
POST /v1/vendor/onboard — Auth: sell scope
{ "url": "https://connect.stripe.com/setup/..." }
Upload release
POST /v1/vendor/releases — Auth: sell scope — Rate limit: 5/min per user
Request: multipart/form-data with:
| Field | Type | Description |
|---|---|---|
archive | file (.zip) | Package archive |
priceCents | number | Price in cents (0 for free) |
Response:
{
"release": {
"id": "uuid",
"version": "1.0.0",
"scanStatus": "pending",
"createdAt": "2026-01-15T10:00:00.000Z"
},
"listing": { "id": "uuid", "slug": "my-skill" }
}
Publish release
PATCH /v1/vendor/releases/:id/publish — Auth: sell scope
{ "ok": true, "version": "1.0.0" }
Update listing
PATCH /v1/vendor/listings/:id — Auth: sell scope
Request body (all fields optional):
{
"title": "My Awesome Skill", // 1–120 chars
"description": "...", // 1–1024 chars
"tags": ["react", "testing"], // max 10 tags
"priceCents": 500, // 0 = free
"readmeMd": "# My Skill\n...", // raw Markdown for listing page
"status": "published" // optional: "published" | "unlisted" | "draft"
}
Get vendor analytics
GET /v1/vendor/analytics — Auth: sell scope
{
"vendor": {
"slug": "acme",
"displayName": "Acme Corp",
"stripeOnboardingComplete": true
},
"revenue": {
"totalCents": 15000,
"totalOrders": 42
},
"listings": [
{
"id": "uuid",
"slug": "router-eval",
"title": "Router Eval Skill",
"priceCents": 500,
"installCount": 42,
"orderCount": 38,
"revenueCents": 14250
}
]
}
Spend policies
Spend policies control what agents can purchase autonomously. See Spend policies for the full reference.
List policies
GET /v1/policies — Auth: Bearer token
{
"policies": [
{
"id": "uuid",
"name": "CI agent policy",
"maxPerPurchaseCents": 1000,
"dailyLimitCents": 5000,
"monthlyLimitCents": 20000,
"requireApprovalAboveCents": 500,
"vendorAllowlist": [],
"blockedTypes": [],
"active": true,
"createdAt": "2026-01-01T00:00:00.000Z"
}
]
}
Create policy
POST /v1/policies — Auth: Bearer token
Request body:
{
"name": "CI agent policy", // required, 1–128 chars
"maxPerPurchaseCents": 1000, // optional, cents
"dailyLimitCents": 5000, // optional
"monthlyLimitCents": 20000, // optional
"requireApprovalAboveCents": 500, // optional — require user approval above this price
"vendorAllowlist": [], // optional — empty = all vendors allowed
"blockedTypes": ["bundle"], // optional — block specific package types
"active": true // optional, default: true
}
Update policy
PATCH /v1/policies/:id — Auth: Bearer token
Same fields as create, all optional. Returns updated policy.
Delete policy
DELETE /v1/policies/:id — Auth: Bearer token
{ "ok": true }
Leaderboard
GET /v1/leaderboard
Get the package leaderboard. No authentication required.
Query parameters:
| Parameter | Type | Description |
|---|---|---|
window | all | trending | hot | Ranking window (default: all) |
type | string | Filter by package type |
limit | number | Max results (default: 20, max: 100) |
offset | number | Pagination offset |
{
"entries": [
{
"rank": 1,
"vendor": "acme",
"slug": "router-eval",
"title": "Router Eval Skill",
"type": "skill",
"vendorVerified": true,
"mogRating": 4.8,
"installCount": 1240
}
],
"total": 42
}
Audits
GET /v1/audits
Get scan/audit results for published packages. No authentication required. Useful for transparency tooling and dashboards.
Query parameters:
| Parameter | Type | Description |
|---|---|---|
type | string | Filter by package type |
status | passed | flagged | failed | pending | Filter by scan status |
limit | number | Max results (default: 20, max: 100) |
offset | number | Pagination offset |
{
"audits": [
{
"rank": 1,
"vendor": "acme",
"slug": "router-eval",
"title": "Router Eval Skill",
"type": "skill",
"vendorVerified": true,
"mogRating": 4.8,
"installCount": 124,
"scan": {
"status": "passed",
"label": "safe",
"qualityScore": 87,
"version": "1.0.0",
"scannedAt": "2026-01-15T10:00:00.000Z"
},
"promptGuard": {
"ran": true,
"verified": true,
"perFile": [{ "file": "SKILL.md", "label": "BENIGN", "score": 0.99 }]
}
}
],
"total": 42,
"generatedAt": "2026-02-23T18:00:00.000Z"
}
Webhooks
Stripe webhook
POST /v1/webhooks/stripe
Stripe webhook handler. Verify with the stripe-signature header.
| Event | Effect |
|---|---|
payment_intent.succeeded | Grants entitlement, marks order as paid |
payment_intent.payment_failed | Marks order as failed |
account.updated | Updates vendor Stripe onboarding status |
{ "received": true }