For machines
Documentation for agents
How to read this portfolio programmatically: endpoints, formats, caching, bot policy and the interfaces this site deliberately does not implement.
Last build: 2026-08-02T13:31:08.710Z
Purpose
This is a personal portfolio. Everything an automated client can read here is already published as HTML on the same origin — the machine-readable interfaces exist so that an agent does not have to scrape a styled page to answer a question about this person's work.
Every representation is generated from one typed dataset in the repository. The HTML page, the Markdown twin, the JSON API, the OpenAPI description, the MCP tools and the JSON-LD all read from it, so they cannot disagree about a fact.
Formats and negotiation
HTML and Markdown use distinct URLs to keep intermediary-cache behavior unambiguous. Use `/api/v1/markdown/en` or `/api/v1/markdown/pt` for Markdown.
The explicit routes make the returned representation independent of request headers.
- `curl https://enoquesousa.com/api/v1/markdown/en` — Markdown, `200`.
- `curl https://enoquesousa.com/en` — HTML, `200`, byte-for-byte what a browser gets.
- `curl https://enoquesousa.com/en` — HTML, independent of `Accept`.
- Assets, API routes and `.well-known` documents are never negotiated into Markdown.
The REST API
Public, anonymous and read-only. Responses are `{ data, meta }`; `meta.contentRevision` is a digest of the content, so a client can tell whether a cached copy is still current without diffing payloads.
Every endpoint accepts `?locale=en|pt`, defaulting to `en`. An unrecognized locale is answered with `400` rather than a silent fallback. Unknown project slugs return `404` with the list of real slugs. Unsupported methods return `405` with an `Allow` header.
| Method | Path | Media type | Purpose |
|---|---|---|---|
| GET | /robots.txt | text/plain | Crawl preferences and the Content-Signal policy. |
| GET | /sitemap.xml | application/xml | Indexable HTML pages, with hreflang alternates. |
| GET | /llms.txt | text/plain | Short index of this site for language models. |
| GET | /llms-full.txt | text/plain | The entire portfolio as Markdown. |
| GET | /openapi.json | application/json | OpenAPI 3.1 description of the REST API. |
| GET | /.well-known/api-catalog | application/linkset+json | RFC 9727 catalog pointing at the API, its description, docs and health. |
| GET | /.well-known/agent-skills/index.json | application/json | Index of published Agent Skills, with a digest of each artifact. |
| GET | /.well-known/agent-skills/enoque-sousa-portfolio/SKILL.md | text/markdown | The skill artifact itself. |
| GET | /api/health | application/json | Liveness, schema version and content revision. Never cached. |
| GET | /api/v1 | application/json | Root of the versioned API; lists its own resources. |
| GET | /api/v1/profile | application/json | Name, role, headline, positioning, public links. |
| GET | /api/v1/projects | application/json | Selected work. Filters: ?featured=true, ?tech=Python. |
| GET | /api/v1/projects/{slug} | application/json | A single project by stable slug. |
| GET | /api/v1/skills | application/json | Practice areas, stack by group, flat technology list. |
| GET | /api/v1/experience | application/json | Roles, most recent first, with ISO start and end. |
| GET | /api/v1/certifications | application/json | Published certifications and courses. |
| GET | /api/v1/contact | application/json | Contact channels already printed on the page. |
| GET | /api/v1/markdown/{locale} | text/markdown | The portfolio as Markdown, addressed directly. |
Versioning
The API path carries the major version (`/api/v1`). `meta.schemaVersion` carries the payload semver: a minor bump adds fields, a major bump changes or removes them. Project slugs are stable identifiers and are never reused for a different project.
`meta.updatedAt` is the build timestamp of the running deployment, not a hand-edited date.
Caching
Content only changes when a new build is deployed, so the JSON resources are `public, max-age=600, s-maxage=3600, stale-while-revalidate=86400` and the discovery documents are cached longer. `/api/health` is `no-store` — a cached health check reports the health of the cache.
HTML pages are `private, max-age=0, must-revalidate`. Explicit Markdown routes are publicly cacheable and do not vary by request headers.
Bots and content use
`robots.txt` declares a single policy — `ai-train=no, search=yes, ai-input=yes` — in the wildcard group and, identically, in a group naming the known AI crawlers, because a crawler obeys only its most specific matching group. Nothing is disallowed: this is a portfolio, and being found is the point.
Read that as: index it, quote it, use it to answer questions about this person. No permission is granted to use it as training data. `robots.txt` is a statement of preference to well-behaved clients, not an access control, and it is not treated as one here.
Authentication
There is none, and that is a decision rather than an omission. Nothing here is protected, nothing is writable, and no operation acts on anyone's behalf — so there is no resource an authorization server could protect. No OAuth or OpenID Connect discovery document is published, because publishing one would advertise a flow that does not exist.
Limits and what is absent
MCP discovery is deferred until its transport lifecycle and edge controls complete a separate production audit. The read-only GET endpoints are cacheable and not separately limited.
Deliberately not implemented: OAuth and OpenID Connect discovery, protected-resource metadata, Web Bot Auth signature directories, WebMCP, and the agent-commerce protocols. Each is either inapplicable to a site with no protected resources or dependent on a specification that is not stable enough to publish against. The repository documents the reasoning per item.
Problems with these interfaces
If an endpoint here is wrong, unreachable or describes something inaccurately, the fastest route is email — the same address published in the contact section of the portfolio.