Developers
How to read Necto Consulting as a machine and publish to it: the APIs, errors, limits and versions. Reading is open to anyone; publishing needs a token.
Quickstart
Read a page as markdown, list the endpoints, search the articles, and check an article without writing it:
curl -H "Accept: text/markdown" https://necto-consulting.pages.dev/
curl https://necto-consulting.pages.dev/api/
curl -X POST https://necto-consulting.pages.dev/mcp -H "Content-Type: application/json" -d '{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"search_articles","arguments":{"query":"<your question>"}}}'
curl -X POST "https://necto-consulting.pages.dev/api/content/?dry_run=1" -H "Authorization: Bearer <token>" -H "Content-Type: application/json" -d '{"slug":"a-test","title":"A test","body":"<p>Text</p>","rubric":"<a category>"}'
Public interfaces
| What | Where |
|---|---|
| Markdown of any page | GET any page with Accept: text/markdown |
| llms.txt | https://necto-consulting.pages.dev/llms.txt |
| Sitemap | https://necto-consulting.pages.dev/sitemap.xml |
| RSS and JSON Feed, full text | https://necto-consulting.pages.dev/feed.xml |
| MCP server (read-only tools; MCP 2026-07-28 and 2025-06-18) | POST https://necto-consulting.pages.dev/mcp, card: server-card.json |
| API catalog (RFC 9727) | /.well-known/api-catalog |
| OpenAPI 3.1 | https://necto-consulting.pages.dev/openapi.json |
| ARD catalog | /.well-known/ai-catalog.json |
| Agent skills | /.well-known/agent-skills/index.json |
| How agents authenticate | /.well-known/auth.md |
Content API
JSON over HTTPS. A success answers { "status": "ok", "version": ... }; GET /api/ returns this table as JSON, and openapi.json describes every request and answer.
| Method | Path | Auth | What it does |
|---|---|---|---|
GET | /api/ | none | This list: every endpoint with its method, path and authentication, and where the docs are. |
GET | /api/health | none | Liveness for agents and uptime monitors. No dependency is probed. |
GET | /api/content/version | none | The API version and what the article template renders (the VISIBILIO handshake). |
POST | /api/content/ | Bearer token | Create an article. With dry_run it is validated and nothing is written; with Idempotency-Key a retry returns the first answer. |
GET | /api/content/{slug} | none | One published article. A draft answers 404. |
PUT | /api/content/{slug} | Bearer token | Update an article: only the fields the payload carries change. With dry_run it is validated and merged, and nothing is written. |
DELETE | /api/content/{slug} | Bearer token | Remove an article and its images. |
GET | /api/content/catalog | Bearer token | Categories, tags and every published article (Visibilio Connect), the articles a page at a time: follow next_cursor until it is null (the Link header names the same next page). |
GET | /api/content/tags | Bearer token | The tags of the published articles, as the catalog lists them (Visibilio Connect). |
POST | /api/content/images | Bearer token | Upload one image (multipart/form-data: file, slug, variant; at most 10 MB) into the site's own storage; the answer names its URL. With dry_run it is checked and nothing is written. |
POST | /api/content/catalog | Bearer token | Rebuild the article lists and counts from the stored articles after parallel writes: a dry run answering each change (changes[]) and the catalog it leaves; ?apply=1 writes them. |
POST | /mcp | none | MCP server, JSON-RPC 2.0 over POST: read-only tools (tools/list names them). MCP 2026-07-28, each request on its own (params._meta and the MCP-Protocol-Version, Mcp-Method and Mcp-Name headers; server/discover lists the versions), and MCP 2025-06-18 (initialize first). |
Keys and the sandbox
Tokens are issued by a person, not by an endpoint: write to info@necto-consulting.com with your organisation, what you are building, the expected volume and whether you need to read or to write. There is no self-service registration.
There is no separate test environment. dry_run is the only sandbox: send ?dry_run=1 or "dry_run": true with a create or an update, and the whole payload is validated and the answer says what would be created, or what the article would become, while nothing is written.
A create, an update or a dry run that succeeds may also carry warnings: what the article misses of the body contract that makes a page citable (an answer in its first 60 words, absolute dates instead of "recently", a source for every superlative, sections that read on their own, three questions a client would ask). They are advice: the answer stays 200. A body longer than 100000 characters is stored without the check, and one warning, unchecked, says so.
Errors
Every /api/* error has one shape; errors[] appears when there are several problems. OAuth answers its own RFC 6749 errors and JSON-RPC answers inside its envelope.
{
"status": "error",
"code": "validation_failed",
"message": "The payload failed validation.",
"hint": "errors[] names each field and what it needs; fix them all and send again.",
"docs": "https://necto-consulting.pages.dev/developers/#error-validation_failed",
"errors": [
{
"field": "title",
"message": "required"
}
]
}
| Code | Status | Meaning | What to do |
|---|---|---|---|
bad_request | 400 | The request is malformed. | Check the method, the path and the headers against GET /api/. |
invalid_json | 400 | The body is not valid JSON. | Send a JSON object with Content-Type: application/json. |
validation_failed | 400 | The payload failed validation. | errors[] names each field and what it needs; fix them all and send again. |
unsupported_version | 400 | This API version is not served. | Send X-Content-API-Version: 1, or leave the header out. |
unauthorized | 401 | A valid token is required. | Send Authorization: Bearer <token>. Tokens are issued by a person: see /.well-known/auth.md. |
forbidden | 403 | This request is not allowed. | The token or the origin is not allowed to do this. |
not_found | 404 | Nothing here. | GET /api/ lists every endpoint; a draft article answers 404 in public. |
method_not_allowed | 405 | This path does not accept this method. | The Allow header lists the methods it accepts. |
conflict | 409 | The resource already exists. | An article with this slug exists: update it with PUT /api/content/{slug}. |
idempotency_in_progress | 409 | A request with this Idempotency-Key is still being processed. | Wait the Retry-After seconds and send the same request again. |
payload_too_large | 413 | The body is too large. | An article with its images must stay under 12 MB. |
idempotency_key_reused | 422 | This Idempotency-Key was used with a different body. | Use a new key for a new request; a retry sends the same body with the same key. |
rate_limited | 429 | Too many requests. | Wait the Retry-After seconds; the RateLimit headers show the policy. |
internal_error | 500 | The server failed. | Retry the same request later. A write cut short may be half done: a create answers its retry with 409 and lists the stored article then; GET /api/content/{slug} shows what is stored. |
bad_gateway | 502 | An upstream service failed. | Retry later. |
unavailable | 503 | The service is not available. | Retry later. |
Limits
Per client address, in a fixed window. Every answer of a limited endpoint carries RateLimit-Policy and RateLimit (draft-ietf-httpapi-ratelimit-headers) and X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset (Unix seconds); a refused request answers 429 with Retry-After in seconds.
| Policy | Applies to | Requests | Window |
|---|---|---|---|
content | POST, PUT and DELETE under /api/content/ | 30 | 60 s |
mcp | POST /mcp | 120 | 60 s |
An article with its images must stay under 12 MB.
Retrying a create safely
Send Idempotency-Key: <a unique value> with POST /api/content/ (draft-ietf-httpapi-idempotency-key-header). The same key with the same body returns the first answer for 24 hours, marked Idempotent-Replayed: true; the same key with another body answers 422 idempotency_key_reused; a request still running answers 409 with Retry-After. A 5xx or a 429 is not stored, so its retry runs again, and a dry run is never stored.
Versions and deprecation
This is version 1.1.0 of the Content API; every answer says so in X-Content-API-Version. Send X-Content-API-Version: 1 to lock the major version: a request for another one answers 400 unsupported_version before anything runs. A breaking change gets a new major version. Before an endpoint is removed it carries Deprecation and Sunset headers for at least 90 days, and it is listed here.
No endpoint is deprecated.
