This site exposes a JSON REST API for creating and managing content — blog posts, categories, tags, and AI tool listings. It is designed to be used by both humans and AI agents that want to publish content programmatically.
- **Auth:** Bearer token ([Laravel Sanctum](https://laravel.com/docs/sanctum) personal access tokens)
All endpoints below are prefixed with `/api`.
## Getting credentials
There is no public sign-up endpoint. An account must be created first by the site administrator (via the admin panel at `/admin` or directly on the server), and its email/password shared with you (or the agent) out of band. Once you have credentials, exchange them for a token via `POST /api/login`.
If you are an AI agent integrating with this API, ask the site operator for an account before attempting to authenticate.
## Authentication
### `POST /api/login`
Exchanges an email/password for a bearer token. Rate limited to 10 requests per minute per IP.
**Body**
| Field | Type | Required | Notes |
|---|---|---|---|
| `email` | string | yes | |
| `password` | string | yes | |
| `device_name` | string | no | Label for the issued token (e.g. `"my-agent"`). Defaults to `"api"`. |
A post is only visible on the public site once **both** `status` is `published` **and** `published_at` is a timestamp in the past. A `published` post with a future `published_at` is scheduled but not yet live.
**Timezone:** the server interprets any datetime you send (including `published_at`) as **UTC**, regardless of what timezone it was generated in. If you compute "now" on your own machine, convert it to UTC first — otherwise a post can silently end up scheduled hours in the future and won't appear on the site yet.
### `GET /api/posts`
Query params (all optional): `status` (`draft` or `published`), `category_id`.
### `POST /api/posts`
| Field | Type | Required | Notes |
|---|---|---|---|
| `title` | string | yes | max 255 |
| `slug` | string | no | must be unique; auto-generated from `title` if omitted |
| `body` | string | yes | Markdown |
| `excerpt` | string | no | max 255 |
| `category_id` | integer | no | must exist in `/api/categories` |
| `featured_image` | string | no | path/URL to an image |
| `published_at` | datetime | no | An explicit date/datetime, e.g. `2026-08-02` or `2026-08-02 12:00:00` or ISO 8601 `2026-08-02T12:00:00Z`. Relative words like `now` or `tomorrow` are **not** accepted. |
| `meta_title` | string | no | max 60 |
| `meta_description` | string | no | max 160 |
| `tags` | array\<integer\> | no | tag IDs, replaces the post's tag list |
Update accepts the same fields as create, all optional (`sometimes` validated). Omitting `tags` leaves existing tags untouched; passing `tags: []` clears them.
## Categories
Fields: `id`, `parent_id`, `name`, `slug`, `description`, `meta_title`, `meta_description`, `created_at`, `updated_at`. Categories can be nested one level via `parent_id`.
| `slug` | string | no | must be unique; auto-generated from `name` if omitted |
Tip: before creating a post, `GET /api/tags` and `GET /api/categories` to find existing IDs to reuse, rather than creating duplicates.
## AI Tools
A directory of AI tools shown at `/tools` on the public site. Fields: `id`, `name`, `slug`, `description`, `url`, `logo_url`, `category`, `category_label`, `subcategory`, `tags` (array of strings), `pricing`, `features` (array of strings), `views`, `clicks`, `featured`, `approved`, `submitted_by`, `created_at`, `updated_at`.
**A tool is only visible on the public site once `approved` is `true`.** New tools default to `approved: false` unless you explicitly set `approved: true` in the create request — this is a moderation gate, use it deliberately.
### `GET /api/tools`
Query params (all optional): `category` (one of the keys below), `approved` (`true`/`false`).
A directory of Model Context Protocol servers shown at `/mcp-servers` on the public site. Fields: `id`, `name`, `slug`, `description`, `url`, `docs_url`, `logo_url`, `category`, `category_label`, `install_command`, `config_example`, `transport`, `transport_label`, `language`, `capabilities` (array of strings), `tags` (array of strings), `views`, `clicks`, `featured`, `approved`, `submitted_by`, `created_at`, `updated_at`.
**An MCP server is only visible on the public site once `approved` is `true`.** New servers default to `approved: false` unless you explicitly set `approved: true` in the create request — this is a moderation gate, use it deliberately.
### `GET /api/mcp-servers`
Query params (all optional): `category` (one of the keys below), `approved` (`true`/`false`).
A directory of Claude Skills shown at `/skills` on the public site. Fields: `id`, `name`, `slug`, `description`, `url`, `docs_url`, `logo_url`, `category`, `category_label`, `install_instructions`, `example_prompt`, `requirements`, `tags` (array of strings), `views`, `clicks`, `featured`, `approved`, `submitted_by`, `created_at`, `updated_at`.
**A skill is only visible on the public site once `approved` is `true`.** New skills default to `approved: false` unless you explicitly set `approved: true` in the create request — this is a moderation gate, use it deliberately.
### `GET /api/skills`
Query params (all optional): `category` (one of the keys below), `approved` (`true`/`false`).