# API de conteúdo — WK Tecnologia

Base URL: `https://blog.wktecnologia.com.br`
Tenant: resolvido pelo header `Host` (este domínio = site `wk`).

## Autenticação

```
Authorization: Bearer <CONTENT_API_KEY_WK>
```

Keys são configuradas no Portainer / env do container. Nunca envie a key em query string.

## Endpoints

### `GET /api/v1/docs`
Documentação desta API (Markdown). **Público.**

### `GET /api/v1/openapi.json`
Especificação OpenAPI 3.0. **Público.**

### `GET /api/v1/posts`
Lista posts do tenant (inclui `public`, `unlisted` e `draft`). **Auth obrigatória.**

### `POST /api/v1/posts`
Cria post. **409** se o slug já existir. **Auth obrigatória.**

### `GET /api/v1/posts/{slug}`
Lê um post (frontmatter + body). **Auth obrigatória.**

### `PUT /api/v1/posts/{slug}`
Upsert (cria ou atualiza). O slug da URL prevalece. **Auth obrigatória.**

### `DELETE /api/v1/posts/{slug}`
Remove o arquivo MDX. **Auth obrigatória.**

## Visibilidade (landings)

| `visibility` | Home / RSS / sitemap / llms | URL direta `/posts/{slug}` |
|----------------|-----------------------------|-----------------------------|
| `public` | sim | sim |
| `unlisted` | **não** (landing) | **sim** |
| `draft` | não | não (prod); sim na API / dev |

`draft: true` (legado) equivale a `visibility: "draft"`.
Sem `visibility` e `draft: false` → `public`.

## Landings (`visibility: "unlisted"`)

Layout de marketing (hero + CTAs), não de artigo. Campos opcionais:

| Campo | Uso |
|-------|-----|
| eyebrow | Label acima do título |
| ctaLabel / ctaHref | CTA primário do hero e rodapé |
| ctaSecondaryLabel / ctaSecondaryHref | CTA secundário |
| ctaFooterTitle / ctaFooterDescription | Faixa final de conversão |
| cover | Imagem do hero |

Componentes MDX no `body`:

```mdx
<CTAGroup>
  <CTA href="https://cheg.ai" variant="primary">Agendar demo</CTA>
  <CTA href="#detalhes" variant="ghost">Ver detalhes</CTA>
</CTAGroup>

<Callout title="Por que agora" tone="accent">
Texto de reforço da oferta.
</Callout>

<FeatureGrid>
  <Feature title="Governança">Controle e auditoria.</Feature>
  <Feature title="Agentes">Marketplace pronto.</Feature>
</FeatureGrid>

<Stats>
  <Stat value="50%" label="Menos atrito" />
  <Stat value="4x" label="Mais velocidade" />
</Stats>
```

Variantes de `CTA`: `primary` | `secondary` | `ghost`.

### `GET /api/v1/media`
Lista imagens do tenant. **Auth obrigatória.**

### `POST /api/v1/media`
Upload multipart (`file`). Tipos: jpeg, png, gif, webp. Máx. 5 MB. **Auth obrigatória.**

### `DELETE /api/v1/media/{filename}`
Remove arquivo de mídia. **Auth obrigatória.**

### `GET /media/{filename}`
Serve a imagem publicamente (sem auth). Use a URL retornada no upload dentro do Markdown:

```md
![Legenda](https://blog.cheg.ai/media/1739-abc-foto.jpg)
```

## Body (POST / PUT)

```json
{
  "title": "Título do artigo",
  "description": "Resumo curto (SEO / llms.txt)",
  "date": "2026-07-22",
  "author": "WK Tecnologia",
  "tags": ["tag-a", "tag-b"],
  "slug": "meu-artigo",
  "visibility": "public",
  "draft": false,
  "cover": "https://blog.wktecnologia.com.br/media/capa.jpg",
  "body": "## Seção\n\nConteúdo em Markdown/MDX."
}
```

| Campo | Tipo | Regras |
|-------|------|--------|
| title | string | 1–200 |
| description | string | 1–500 |
| date | string | ISO ou data legível |
| author | string | 1–120 |
| tags | string[] | opcional |
| slug | string | kebab-case `[a-z0-9-]+` |
| visibility | string | `public` \| `unlisted` \| `draft` (default `public`) |
| draft | boolean | legado; `true` ⇒ draft |
| body | string | Markdown/MDX, obrigatório |
| cover | string | opcional — URL `https://…` ou path `/media/….jpg` (OG + capa) |
| eyebrow | string | landing — label acima do título |
| ctaLabel | string | landing — texto do CTA primário |
| ctaHref | string | landing — URL do CTA primário |
| ctaSecondaryLabel | string | landing — texto do CTA secundário |
| ctaSecondaryHref | string | landing — URL do CTA secundário |
| ctaFooterTitle | string | landing — título da faixa final |
| ctaFooterDescription | string | landing — texto da faixa final |

No Markdown/MDX do body (artigos e landings):

```md
![Legenda da figura](/media/1739-abc-foto.jpg)
```

## Exemplos completos (receitas)

Substitua `$CONTENT_API_KEY` pela key do tenant (`CONTENT_API_KEY_WK`).

### 1) Upload de mídia → URL pública

```bash
curl -s -X POST https://blog.wktecnologia.com.br/api/v1/media \
  -H "Authorization: Bearer $CONTENT_API_KEY" \
  -F "file=@./capa.jpg;type=image/jpeg"

# Resposta:
# { "data": { "filename": "…-capa.jpg", "url": "https://blog.wktecnologia.com.br/media/…-capa.jpg", "size": 12345, "contentType": "image/jpeg" } }
```

Use `data.url` em `cover` e em `![alt](url)` no body.

### 2) Artigo público com cover + imagem no conteúdo

```bash
# 1) upload capa e figura (guarde as URLs retornadas)
COVER=$(curl -s -X POST https://blog.wktecnologia.com.br/api/v1/media -H "Authorization: Bearer $CONTENT_API_KEY" -F "file=@./capa.jpg;type=image/jpeg" | node -e "let d='';process.stdin.on('data',c=>d+=c);process.stdin.on('end',()=>console.log(JSON.parse(d).data.url))")
FIG=$(curl -s -X POST https://blog.wktecnologia.com.br/api/v1/media -H "Authorization: Bearer $CONTENT_API_KEY" -F "file=@./figura.jpg;type=image/jpeg" | node -e "let d='';process.stdin.on('data',c=>d+=c);process.stdin.on('end',()=>console.log(JSON.parse(d).data.url))")

# 2) criar post
curl -s -X POST https://blog.wktecnologia.com.br/api/v1/posts \
  -H "Authorization: Bearer $CONTENT_API_KEY" \
  -H "Content-Type: application/json" \
  -d "{
    \"title\": \"Guia de SEO técnico\",
    \"description\": \"Checklist prático para hubs de conteúdo.\",
    \"date\": \"2026-07-22\",
    \"author\": \"WK Tecnologia\",
    \"tags\": [\"seo\", \"conteudo\"],
    \"slug\": \"guia-seo-tecnico\",
    \"visibility\": \"public\",
    \"cover\": \"$COVER\",
    \"body\": \"## Introdução\\n\\nTexto do artigo.\\n\\n![Diagrama]($FIG)\\n\\n## Conclusão\\n\\nPróximos passos.\"
  }"
```

Página: `https://blog.wktecnologia.com.br/posts/guia-seo-tecnico` (aparece na home).

### 3) Landing de marketing (unlisted) com CTAs

```bash
curl -s -X POST https://blog.wktecnologia.com.br/api/v1/posts \
  -H "Authorization: Bearer $CONTENT_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "title": "Operacione agentes com governança",
    "description": "IA com controle para times que precisam entregar em produção.",
    "date": "2026-07-22",
    "author": "WK Tecnologia",
    "tags": ["landing", "produto"],
    "slug": "campanha-agentes",
    "visibility": "unlisted",
    "eyebrow": "Produto",
    "ctaLabel": "Agendar demo",
    "ctaHref": "https://cheg.ai",
    "ctaSecondaryLabel": "Ver plataforma",
    "ctaSecondaryHref": "https://cheg.ai",
    "ctaFooterTitle": "Comece com a Chegai",
    "ctaFooterDescription": "Fale com um especialista e veja agentes em produção.",
    "body": "## O que você ganha\n\n<FeatureGrid>\n  <Feature title=\"Governança\">Aprovações e trilha de auditoria.</Feature>\n  <Feature title=\"Velocidade\">Do briefing ao agente em dias.</Feature>\n  <Feature title=\"Marketplace\">Agentes prontos para o seu stack.</Feature>\n  <Feature title=\"Segurança\">Controles para operação responsável.</Feature>\n</FeatureGrid>\n\n<Stats>\n  <Stat value=\"50%\" label=\"Menos atrito\" />\n  <Stat value=\"4x\" label=\"Mais velocidade\" />\n</Stats>\n\n<Callout title=\"Por que agora\" tone=\"accent\">\nMenos atrito entre produto, segurança e operação.\n</Callout>\n\n<CTAGroup>\n  <CTA href=\"https://cheg.ai\" variant=\"primary\">Falar com especialista</CTA>\n  <CTA href=\"https://cheg.ai\" variant=\"ghost\">Explorar marketplace</CTA>\n</CTAGroup>"
  }'
```

Página: `https://blog.wktecnologia.com.br/posts/campanha-agentes` (não aparece na home / RSS / sitemap / llms).

### 4) Operações CRUD

```bash
# Listar (todos: public + unlisted + draft)
curl -s https://blog.wktecnologia.com.br/api/v1/posts \
  -H "Authorization: Bearer $CONTENT_API_KEY"

# Ler um
curl -s https://blog.wktecnologia.com.br/api/v1/posts/campanha-agentes \
  -H "Authorization: Bearer $CONTENT_API_KEY"

# Atualizar (upsert)
curl -s -X PUT https://blog.wktecnologia.com.br/api/v1/posts/campanha-agentes \
  -H "Authorization: Bearer $CONTENT_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ ...mesmo shape do POST... }'

# Remover post
curl -s -X DELETE https://blog.wktecnologia.com.br/api/v1/posts/campanha-agentes \
  -H "Authorization: Bearer $CONTENT_API_KEY"

# Remover mídia
curl -s -X DELETE https://blog.wktecnologia.com.br/api/v1/media/1784-abc-capa.jpg \
  -H "Authorization: Bearer $CONTENT_API_KEY"

# Listar mídias
curl -s https://blog.wktecnologia.com.br/api/v1/media \
  -H "Authorization: Bearer $CONTENT_API_KEY"
```

## Respostas de erro

```json
{ "error": { "code": "unauthorized|validation_error|conflict|not_found|rate_limited|invalid_json|upload_failed", "message": "..." } }
```

| HTTP | Código |
|------|--------|
| 400 | validation_error, invalid_json, invalid_form |
| 401 | unauthorized |
| 404 | not_found |
| 409 | conflict |
| 429 | rate_limited |
| 500 | upload_failed |

## Persistência

Posts `.mdx` e mídia em `posts/_media/` no volume Docker do tenant.  
Após mutação, home, post, sitemap, rss e llms.* são revalidados.

## Artefatos públicos (sem auth)

- https://blog.wktecnologia.com.br/api/v1/docs
- https://blog.wktecnologia.com.br/api/v1/openapi.json
- https://blog.wktecnologia.com.br/llms.txt
- https://blog.wktecnologia.com.br/llms-full.txt
- https://blog.wktecnologia.com.br/rss.xml
- https://blog.wktecnologia.com.br/sitemap.xml
- https://blog.wktecnologia.com.br/robots.txt
