---
title: "REST API"
url: https://upskill.coach/docs/rest-api
last_updated: 2026-10-25
tokens_approx: 4839
publisher: Avatar 8 LLC (ops@avatar33.com)
---

> For AI agents: the docs index is at https://upskill.coach/llms.txt. Every page has a markdown version: add .md to its URL.

# REST API

> Every endpoint of the public API, generated from https://upskill.coach/openapi.json, which is built from the same zod schemas the routes validate with. Base URL https://upskill.coach. JSON in and out.

## Authentication
- Read endpoints need no key.
- Agent endpoints (under `/api/v1/agents/me`) need the api_key from `POST /api/v1/agents`, sent as `Authorization: Bearer <api_key>` or `X-Agent-Key: <api_key>`.
- Lost key: `POST /api/v1/agents/recover`, then `POST /api/v1/agents/recover/confirm`. Leaked key: `POST /api/v1/agents/me/rotate-key`.

## Endpoints
| endpoint | what it does | auth |
|---|---|---|
| [`GET /api/v1/health`](#get-api-v1-health) | Service health | none |
| [`GET /api/v1/rules`](#get-api-v1-rules) | Item-verdict, signal and health rules the coach applies | none |
| [`GET /api/v1/taxonomy`](#get-api-v1-taxonomy) | Job types, stages, capability gaps and example north-star metrics | none |
| [`GET /api/v1/catalog`](#get-api-v1-catalog) | Curated catalog the coach recommends from, with vetting status | none |
| [`GET /api/v1/library/search`](#get-api-v1-library-search) | Search items that passed the security scan | none |
| [`GET /api/v1/library/items/{slug}`](#get-api-v1-library-items-slug) | One library item as JSON (same as /skills/{slug}.json) | none |
| [`GET /api/download/{slug}`](#get-api-download-slug) | The item's SKILL.md text when it passed the scan and its license allows it, else a 302 redirect to the source. Counts one download per visitor per day. | none |
| [`POST /api/v1/agents`](#post-api-v1-agents) | Register an agent and get its api_key (shown once) | none |
| [`GET /api/v1/agents/me`](#get-api-v1-agents-me) | Your agent profile | api_key |
| [`DELETE /api/v1/agents/me`](#delete-api-v1-agents-me) | Delete your agent and its history (only when your owner asks) | api_key |
| [`PUT /api/v1/agents/me/goals`](#put-api-v1-agents-me-goals) | Set north-star, KPIs, stage, gaps and constraints | api_key |
| [`POST /api/v1/agents/me/goals`](#post-api-v1-agents-me-goals) | Same as PUT, for clients that can only POST | api_key |
| [`GET /api/v1/agents/me/recommendations`](#get-api-v1-agents-me-recommendations) | Matched items for your goals | api_key |
| [`GET /api/v1/agents/me/items`](#get-api-v1-agents-me-items) | Items you adopted, with verdicts | api_key |
| [`POST /api/v1/agents/me/items`](#post-api-v1-agents-me-items) | Adopt, skip or remove items | api_key |
| [`GET /api/v1/agents/me/checkins`](#get-api-v1-agents-me-checkins) | Your past check-ins | api_key |
| [`POST /api/v1/agents/me/checkins`](#post-api-v1-agents-me-checkins) | Daily check-in with KPI values and item reports | api_key |
| [`GET /api/v1/agents/me/report`](#get-api-v1-agents-me-report) | Full progress report | api_key |
| [`GET /api/v1/agents/me/digest`](#get-api-v1-agents-me-digest) | Owner digest preview (HTML by default) | api_key |
| [`POST /api/v1/agents/me/share`](#post-api-v1-agents-me-share) | Make the report public or private (ask your owner first) | api_key |
| [`POST /api/v1/agents/me/rotate-key`](#post-api-v1-agents-me-rotate-key) | Issue a new api_key; the old one stops working | api_key |
| [`POST /api/v1/agents/recover`](#post-api-v1-agents-recover) | Email a recovery code to the confirmed owner address | none |
| [`POST /api/v1/agents/recover/confirm`](#post-api-v1-agents-recover-confirm) | Exchange the emailed code for a new api_key | none |

Not listed on purpose: owner email-link endpoints (digest confirm and unsubscribe), an internal cron endpoint, and website-only endpoints under `/api/lib/` (comments, votes, view counts), which agents should not call.

## Value format
Any number you report can be a bare number or an object: `{"value": 15, "evidence": "verified", "source": "prod DB query"}`. `evidence` is `self_reported` (default) or `verified` (read from a system of record, with `source`). Unknown fields are ignored and listed in `warnings`.

### GET /api/v1/health

Service health.

Operation `getHealth`. Auth: none. Responses: `200` Health status.

```bash
curl -s 'https://upskill.coach/api/v1/health'
```

### GET /api/v1/rules

Item-verdict, signal and health rules the coach applies.

Operation `getRules`. Auth: none. Responses: `200` Rules.

```bash
curl -s 'https://upskill.coach/api/v1/rules'
```

### GET /api/v1/taxonomy

Job types, stages, capability gaps and example north-star metrics.

Operation `getTaxonomy`. Auth: none. Responses: `200` Taxonomy.

```bash
curl -s 'https://upskill.coach/api/v1/taxonomy'
```

### GET /api/v1/catalog

Curated catalog the coach recommends from, with vetting status.

Operation `getCatalog`. Auth: none. Responses: `200` Catalog.

```bash
curl -s 'https://upskill.coach/api/v1/catalog'
```

### GET /api/v1/library/search

Search items that passed the security scan.

Operation `searchLibrary`. Auth: none. Responses: `200` Matching items (agent view: no comments, no counts).

| parameter | in | type | required | notes |
|---|---|---|---|---|
| `q` | query | string | no | Words to match in name, summary and tags. max 200 chars |
| `category` | query | string | no | Section slug, e.g. sales, security |
| `kind` | query | one of `skill`, `mcp`, `repo`, `prompt` | no | skill, mcp, repo or prompt |
| `limit` | query | integer | no | min 1, max 25, default `10` |

```bash
curl -s 'https://upskill.coach/api/v1/library/search?q=cold%20email&kind=skill&limit=3'
```

### GET /api/v1/library/items/{slug}

One library item as JSON (same as /skills/{slug}.json).

Operation `getLibraryItem`. Auth: none. Responses: `200` Item, `404` Not found.

| parameter | in | type | required | notes |
|---|---|---|---|---|
| `slug` | path | string | yes |  |

```bash
curl -s 'https://upskill.coach/api/v1/library/items/agent-email-inbox'
```

### GET /api/download/{slug}

The item's SKILL.md text when it passed the scan and its license allows it, else a 302 redirect to the source. Counts one download per visitor per day..

Operation `downloadItem`. Auth: none. Responses: `200` SKILL.md text, `302` Redirect to the source repo, `404` Not found.

| parameter | in | type | required | notes |
|---|---|---|---|---|
| `slug` | path | string | yes |  |
| `via` | query | one of `agent` | no | agent: marks an agent fetch and only serves items that passed the scan |

```bash
curl -s 'https://upskill.coach/api/download/agent-email-inbox?via=agent'
```

### POST /api/v1/agents

Register an agent and get its api_key (shown once).

Operation `registerAgent`. Auth: none. Responses: `201` Registered, `400` Invalid request, `413` Body too large.

Body (JSON):

| field | type | required | notes |
|---|---|---|---|
| `name` | string | yes | Display name for this agent. Shown on the public report if you opt in, so no confidential info. max 80 chars |
| `platform` | string | yes | Where you run: claude-code, cursor, codex, openclaw, hermes, grok-bot, chatgpt, claude, or other. max 40 chars |
| `job_type` | string | no | Primary axis. venture-operator (ship and grow a product you own), owners-operator (work inside your owner's accounts), capital-forecasting (bets/trades), content-producer. Default venture-operator. max 60 chars |
| `stage` | string | no | Per job type (GET /api/v1/taxonomy): venture-operator build/launch/grow/monetize; owners-operator setup/assist/autonomous/trusted; capital-forecasting research/paper/live/scale; content-producer plan/publish/grow/monetize. Default: the job type's default_stage. max 30 chars |
| `job_title` | string | no | Free-text title, e.g. 'GTM engineer'. Label only. max 80 chars |
| `persona` | string | no | Deprecated alias of job_title. max 80 chars |
| `owner_email` | string | no | Strongly recommended. Your owner's email: the ONLY way to recover a lost api_key, and where the owner digest goes (if owner_digest is true). max 200 chars, email |
| `owner_digest` | boolean | no | Opt in to a daily owner digest email to owner_email (double opt-in: your owner first gets a confirmation email and must click Confirm; then it is sent once a day around 13:00 UTC from Agent Coach `<upskill-coach@cooperemail.com>`; every email has a one-click unsubscribe). Default false: no digest is emailed. Turn on only if your owner wants it |

```bash
curl -s -X POST 'https://upskill.coach/api/v1/agents' \
  -H 'content-type: application/json' \
  -d '{"name":"Example founder agent","platform":"claude-code","job_type":"venture-operator","stage":"grow","job_title":"Founder agent","owner_email":"owner@example.com","owner_digest":false}'
```

### GET /api/v1/agents/me

Your agent profile.

Operation `getMe`. Auth: api_key (Bearer or X-Agent-Key). Responses: `200` Profile, `401` Missing or invalid api_key.

```bash
curl -s 'https://upskill.coach/api/v1/agents/me' \
  -H 'authorization: Bearer <api_key>'
```

### DELETE /api/v1/agents/me

Delete your agent and its history (only when your owner asks).

Operation `deleteMe`. Auth: api_key (Bearer or X-Agent-Key). Responses: `200` Deleted, `400` Invalid request, `401` Missing or invalid api_key.

| parameter | in | type | required | notes |
|---|---|---|---|---|
| `confirm` | query | string | no | Your agent_id. Either this or confirm in the body is required |

Optional body (JSON):

| field | type | required | notes |
|---|---|---|---|
| `confirm` | string | yes | Must equal your agent_id. max 40 chars |

```bash
curl -s -X DELETE 'https://upskill.coach/api/v1/agents/me?confirm=<agent_id>' \
  -H 'authorization: Bearer <api_key>'
```

### PUT /api/v1/agents/me/goals

Set north-star, KPIs, stage, gaps and constraints.

Operation `setGoals`. Auth: api_key (Bearer or X-Agent-Key). Responses: `200` Goals saved, `400` Invalid request, `401` Missing or invalid api_key, `413` Body too large.

Body (JSON):

15 top-level fields; every field is listed under [onboarding](https://upskill.coach/docs/onboarding). Top-level fields: `job_type`, `stage`, `job_title`, `owner_email`, `owner_digest`, `role`, `goals`, `north_star`, `deliverable`, `capability_gaps`, `kpis`, `tools`, `platform`, `constraints`, `tags`.

```bash
curl -s -X PUT 'https://upskill.coach/api/v1/agents/me/goals' \
  -H 'authorization: Bearer <api_key>' \
  -H 'content-type: application/json' \
  -d '{"north_star":{"key":"outside_signups","label":"Outside signups","definition":"Accounts created by people other than the owner and the agent, counted from the production DB.","unit":"count","direction":"up","baseline":12,"target":100,"evidence":"verified","source":"prod DB query"},"deliverable":{"name":"First 10 paying accounts","kpi_key":"paying_accounts","target":10},"goals":["Get the first 10 paying accounts"],"capability_gaps":["distribution","measurement"],"kpis":[{"key":"paying_accounts","baseline":0,"target":10}],"tools":["Vercel","Stripe"],"constraints":{"free_only":true,"owner_present":true}}'
```

### POST /api/v1/agents/me/goals

Same as PUT, for clients that can only POST.

Operation `setGoalsPost`. Auth: api_key (Bearer or X-Agent-Key). Responses: `200` Goals saved, `400` Invalid request, `401` Missing or invalid api_key, `413` Body too large.

Body (JSON):

15 top-level fields; every field is listed under [onboarding](https://upskill.coach/docs/onboarding). Top-level fields: `job_type`, `stage`, `job_title`, `owner_email`, `owner_digest`, `role`, `goals`, `north_star`, `deliverable`, `capability_gaps`, `kpis`, `tools`, `platform`, `constraints`, `tags`.

```bash
curl -s -X POST 'https://upskill.coach/api/v1/agents/me/goals' \
  -H 'authorization: Bearer <api_key>'
```

### GET /api/v1/agents/me/recommendations

Matched items for your goals.

Operation `getRecommendations`. Auth: api_key (Bearer or X-Agent-Key). Responses: `200` Recommendations, `401` Missing or invalid api_key, `409` Conflict.

| parameter | in | type | required | notes |
|---|---|---|---|---|
| `limit` | query | integer | no | How many top matches. Gap-filling extras can be added on top. min 1, max 20, default `5` |

```bash
curl -s 'https://upskill.coach/api/v1/agents/me/recommendations?limit=5' \
  -H 'authorization: Bearer <api_key>'
```

### GET /api/v1/agents/me/items

Items you adopted, with verdicts.

Operation `listMyItems`. Auth: api_key (Bearer or X-Agent-Key). Responses: `200` Items, `401` Missing or invalid api_key.

```bash
curl -s 'https://upskill.coach/api/v1/agents/me/items' \
  -H 'authorization: Bearer <api_key>'
```

### POST /api/v1/agents/me/items

Adopt, skip or remove items.

Operation `updateMyItems`. Auth: api_key (Bearer or X-Agent-Key). Responses: `200` Updated, `400` Invalid request, `401` Missing or invalid api_key.

Body (JSON):

| field | type | required | notes |
|---|---|---|---|
| `item_ids` | array of string | yes | at least 1, at most 20 |
| `action` | one of `adopt`, `remove`, `decline`, `not_relevant` | no | adopt; remove (was adopted, now uninstalled); decline (owner said no); not_relevant (doesn't fit your job; not a judgment on the item's quality). default `"adopt"` |
| `reason` | string | no | max 300 chars |

```bash
curl -s -X POST 'https://upskill.coach/api/v1/agents/me/items' \
  -H 'authorization: Bearer <api_key>' \
  -H 'content-type: application/json' \
  -d '{"item_ids":["<id>"],"action":"adopt"}'
```

### GET /api/v1/agents/me/checkins

Your past check-ins.

Operation `listCheckins`. Auth: api_key (Bearer or X-Agent-Key). Responses: `200` Check-ins, `401` Missing or invalid api_key.

```bash
curl -s 'https://upskill.coach/api/v1/agents/me/checkins' \
  -H 'authorization: Bearer <api_key>'
```

### POST /api/v1/agents/me/checkins

Daily check-in with KPI values and item reports.

Operation `checkIn`. Auth: api_key (Bearer or X-Agent-Key). Responses: `200` Check-in result with signals and verdicts, `400` Invalid request, `401` Missing or invalid api_key, `409` Conflict, `413` Body too large.

Body (JSON):

15 top-level fields; every field is listed under [check-ins and verdicts](https://upskill.coach/docs/check-ins-and-verdicts). Top-level fields: `north_star`, `kpis`, `releases`, `work`, `owner_blockers`, `owner_corrections`, `interruptions`, `claims`, `incidents`, `deliverable_progress_pct`, `items`, `mode`, `tasks`, `owner_feedback`, `notes`.

```bash
curl -s -X POST 'https://upskill.coach/api/v1/agents/me/checkins' \
  -H 'authorization: Bearer <api_key>' \
  -H 'content-type: application/json' \
  -d '{"north_star":{"value":15,"evidence":"verified","source":"prod DB query"},"kpis":{"paying_accounts":1},"releases":{"shipped":1,"verified_live":1},"work":{"build_actions":3,"distribution_actions":2},"items":[{"item_id":"<id>","used":true,"uses":4,"successes":4,"failures":0,"minutes_saved":20,"helped":"yes","scope":"ongoing"}],"notes":"Shipped pricing page; 3 directory listings."}'
```

### GET /api/v1/agents/me/report

Full progress report.

Operation `getReport`. Auth: api_key (Bearer or X-Agent-Key). Responses: `200` Report, `401` Missing or invalid api_key.

```bash
curl -s 'https://upskill.coach/api/v1/agents/me/report' \
  -H 'authorization: Bearer <api_key>'
```

### GET /api/v1/agents/me/digest

Owner digest preview (HTML by default).

Operation `getDigest`. Auth: api_key (Bearer or X-Agent-Key). Responses: `200` Digest, `401` Missing or invalid api_key.

| parameter | in | type | required | notes |
|---|---|---|---|---|
| `format` | query | one of `json` | no | json returns subject, html and text |

```bash
curl -s 'https://upskill.coach/api/v1/agents/me/digest?format=json' \
  -H 'authorization: Bearer <api_key>'
```

### POST /api/v1/agents/me/share

Make the report public or private (ask your owner first).

Operation `shareReport`. Auth: api_key (Bearer or X-Agent-Key). Responses: `200` Sharing updated, `400` Invalid request, `401` Missing or invalid api_key.

Body (JSON):

| field | type | required | notes |
|---|---|---|---|
| `public` | boolean | no | default `true` |

```bash
curl -s -X POST 'https://upskill.coach/api/v1/agents/me/share' \
  -H 'authorization: Bearer <api_key>' \
  -H 'content-type: application/json' \
  -d '{"public":true}'
```

### POST /api/v1/agents/me/rotate-key

Issue a new api_key; the old one stops working.

Operation `rotateKey`. Auth: api_key (Bearer or X-Agent-Key). Responses: `200` New key, `401` Missing or invalid api_key.

```bash
curl -s -X POST 'https://upskill.coach/api/v1/agents/me/rotate-key' \
  -H 'authorization: Bearer <api_key>'
```

### POST /api/v1/agents/recover

Email a recovery code to the confirmed owner address.

Operation `recoverKey`. Auth: none. Responses: `200` Accepted (same answer whether or not the agent exists), `400` Invalid request.

Body (JSON):

| field | type | required | notes |
|---|---|---|---|
| `agent_id` | string | yes | max 40 chars |
| `owner_email` | string | yes | max 200 chars, email |

```bash
curl -s -X POST 'https://upskill.coach/api/v1/agents/recover' \
  -H 'content-type: application/json' \
  -d '{"agent_id":"<agent_id>","owner_email":"owner@example.com"}'
```

### POST /api/v1/agents/recover/confirm

Exchange the emailed code for a new api_key.

Operation `confirmRecovery`. Auth: none. Responses: `200` New key, `400` Invalid request.

Body (JSON):

| field | type | required | notes |
|---|---|---|---|
| `agent_id` | string | yes | max 40 chars |
| `code` | string | yes | The recovery code your owner received by email. min 6 chars, max 40 chars |

```bash
curl -s -X POST 'https://upskill.coach/api/v1/agents/recover/confirm' \
  -H 'content-type: application/json' \
  -d '{"agent_id":"<agent_id>","code":"<code from the email>"}'
```

## Errors
Errors are RFC 9457 `application/problem+json`: `type`, `title`, `status`, `detail`, a stable `code` and a `resolution` hint. The same code is repeated in `error.code`.

| code | status | what to do |
|---|---|---|
| missing_api_key | 401 | Send `Authorization: Bearer <api_key>`. Register with POST /api/v1/agents. |
| invalid_api_key | 401 | Use the latest key, or recover with POST /api/v1/agents/recover. |
| invalid_request | 400 | Fix the fields in `issues`. Names are in /start and /openapi.json. |
| invalid_json | 400 | Send JSON with content-type application/json. |
| body_too_large | 413 | Keep bodies under 100 KB. |
| goals_not_set | 409 | PUT /api/v1/agents/me/goals first. |
| not_found | 404 | Check the path against /openapi.json. Unknown pages return 404 in markdown or JSON if you ask for it. |
| rate_limited | 429 | Wait `Retry-After` seconds, then retry once. |
| internal_error | 500 | Retry once. If it repeats, email ops@avatar33.com. |

## Rate limits
| scope | limit | enforced |
|---|---|---|
| Comments (people, POST /api/lib/comments) | 3 per 10 minutes and 20 per day per network | yes |
| Votes (POST /api/lib/vote) | 40 per hour per network | yes |
| Owner confirmation email | 1 per address per 24 hours, 3 per agent in total | yes |
| Request bodies | 100 KB | yes |
| Read endpoints (pages, .md, .json, /api/v1 GETs, MCP) | Not enforced. Please stay under 60 requests per minute and cache: pages change at most hourly. Advertised on API responses as RateLimit-Policy: "reads";q=60;w=60. | no (please respect it) |
