> ## Documentation Index
> Fetch the complete documentation index at: https://docs.cativa.digital/llms.txt
> Use this file to discover all available pages before exploring further.

# Cursos e certificados

> A hierarquia Curso > Módulo > Aula, matrícula e progresso, acesso via badge e verificação pública de certificados.

Um **Curso** na Cativa é uma sequência de aulas organizada em módulos, vive dentro de um Grupo e entrega um **certificado** quando o aluno conclui. O acesso ao curso é controlado por badge, e cada certificado tem um código verificável publicamente, sem chave, útil para páginas de "validar certificado".

Pense no curso como um livro: o **módulo** é o capítulo, a **aula** é a página. O aluno vira página por página, e o progresso é simplesmente quantas páginas já leu. A **carteirinha da biblioteca** (o badge) é o que libera pegar o livro: sem ela, nem dá para abrir.

## A hierarquia

```
Curso (ex: "Mentoria 2026")
└── Módulo (bloco temático, ex: "Fundamentos")
    └── Aula (unidade de conteúdo: vídeo, texto, etc.)
```

Um **Curso** agrupa **Módulos**, cada Módulo agrupa **Aulas**. O aluno avança aula a aula, e o progresso é medido pela proporção de aulas concluídas. Quando o curso é concluído, o certificado é emitido.

<Note>
  Curso é uma entidade estática definida pelo admin do tenant, no mesmo espírito de Grupos e Espaços. A criação de estrutura (curso, módulo, aula) exige escopo administrativo. Veja [Comunidades e espaços](/pt-BR/concepts/communities-and-spaces) para a regra completa de quem cria o quê.
</Note>

## Acesso ao curso: badge libera

Assim como Grupos, um Curso é liberado por **badge**. O aluno só matricula e consome as aulas se tiver o badge exigido pelo curso. Isso é configurado no painel admin, não pela API do parceiro.

```
Curso "Mentoria 2026"  ──exige──> Badge "Premium"
Curso "Introdução"     ──não exige badge──> qualquer usuário matricula
```

O parceiro **não configura** a regra de acesso, mas **dispara** a transição: ao atribuir o badge ao usuário, ele ganha acesso ao curso e pode se matricular. Na prática, a entrega de acesso quase sempre vem de um badge atrelado a uma compra ou assinatura. Veja o guia [Liberar acesso via compra](/pt-BR/guides/grant-access-via-purchase).

<Warning>
  Não trate a matrícula como o mecanismo de acesso. O acesso é do badge. Matricular sem o badge correspondente retorna `403 forbidden`. Modele a liberação sempre pelo badge, nunca criando um curso por cliente.
</Warning>

## Base e autenticação

Todas as chamadas usam a base pública da API:

```
https://apis.cativalab.digital/tenant/api/v2
```

Autenticação por API Key no header, exceto o endpoint público de validação de certificado (detalhado adiante):

```
Authorization: Bearer cativa_live_...
```

<Note>
  Os IDs são ULIDs (ex: `01HQ5ABCDEF1234567890XYZ`). Não invente o formato dos payloads de resposta a partir dos exemplos aqui. Para o schema exato de cada campo, consulte a aba **API Reference** e filtre pelas tags **Course** e **Certificate**.
</Note>

## Ler cursos

```bash theme={null}
# Listar cursos disponíveis no tenant
curl https://apis.cativalab.digital/tenant/api/v2/education/courses \
  -H "Authorization: Bearer cativa_live_..."

# Detalhe de um curso específico
curl https://apis.cativalab.digital/tenant/api/v2/education/courses/01HQ5ABCDEF1234567890XYZ \
  -H "Authorization: Bearer cativa_live_..."

# Módulos (e suas aulas) de um curso
curl https://apis.cativalab.digital/tenant/api/v2/education/courses/01HQ5ABCDEF1234567890XYZ/modules \
  -H "Authorization: Bearer cativa_live_..."
```

## Criar um curso

A criação de curso exige escopo administrativo. Chaves de parceiro com escopo padrão não enxergam essa categoria. O fluxo natural é o admin montar a estrutura no painel, mas o endpoint existe para automações administrativas.

<CodeGroup>
  ```bash cURL theme={null}
  curl -X POST https://apis.cativalab.digital/tenant/api/v2/education/courses \
    -H "Authorization: Bearer cativa_live_..." \
    -H "Content-Type: application/json" \
    -d '{
      "groupId": "01HQ5ABCDEF1234567890XYZ",
      "title": "Mentoria 2026",
      "description": "Trilha completa de mentoria."
    }'
  ```

  ```js Node theme={null}
  // groupId aponta o grupo dono do curso. O acesso do aluno vem do badge do curso,
  // configurado depois no painel admin, nao no body deste POST.
  const res = await fetch('https://apis.cativalab.digital/tenant/api/v2/education/courses', {
    method: 'POST',
    headers: {
      'Authorization': `Bearer ${apiKey}`,
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({
      groupId: '01HQ5ABCDEF1234567890XYZ',
      title: 'Mentoria 2026',
      description: 'Trilha completa de mentoria.'
    })
  });
  const course = await res.json();
  ```
</CodeGroup>

Para editar metadados do curso, use `PUT /education/courses/{courseId}` (atualização geral) ou `PATCH /admin/courses/{courseId}` (ajuste administrativo pontual). Para remover, `DELETE /admin/courses/{courseId}`.

### Módulos e aulas (admin)

A estrutura interna do curso é gerenciada sob o prefixo `/admin/education/courses/{courseId}`:

```
POST   /admin/education/courses/{courseId}/modules              # criar modulo
PUT    /admin/education/courses/{courseId}/modules/{moduleId}   # editar modulo
DELETE /admin/education/courses/{courseId}/modules/{moduleId}   # remover modulo

POST   /admin/education/courses/{courseId}/modules/{moduleId}/lessons   # criar aula
PUT    /admin/education/courses/{courseId}/lessons/{lessonId}           # editar aula
DELETE /admin/education/courses/{courseId}/lessons/{lessonId}           # remover aula
```

## Do curso vazio ao aluno matriculado

<Steps>
  <Step title="Criar o curso (admin)">
    `POST /education/courses` com `groupId` e título. Guarde o `courseId` retornado.
  </Step>

  <Step title="Adicionar módulos e aulas (admin)">
    `POST /admin/education/courses/{courseId}/modules` cria o módulo; depois `POST .../modules/{moduleId}/lessons` cria cada aula. Repita até montar a trilha.
  </Step>

  <Step title="Liberar o acesso via badge">
    O acesso do aluno vem do **badge** exigido pelo curso (configurado no painel). Atribua esse badge ao usuário (tipicamente via compra ou assinatura). Veja [Liberar acesso via compra](/pt-BR/guides/grant-access-via-purchase).
  </Step>

  <Step title="Matricular e concluir (aluno)">
    Com o badge, o usuário chama `POST .../enroll`, avança marcando `PUT .../lessons/{lessonId}/complete` e, ao fechar todas as aulas, recebe o certificado.
  </Step>
</Steps>

## Matrícula e progresso (aluno)

Do lado do aluno, o ciclo é matricular, acompanhar progresso e concluir aula por aula.

```bash theme={null}
# Matricular o usuario da credencial no curso
curl -X POST https://apis.cativalab.digital/tenant/api/v2/education/courses/01HQ5.../enroll \
  -H "Authorization: Bearer cativa_live_..."

# Ler progresso do usuario no curso
curl https://apis.cativalab.digital/tenant/api/v2/education/courses/01HQ5.../progress \
  -H "Authorization: Bearer cativa_live_..."

# Marcar uma aula como concluida
curl -X PUT https://apis.cativalab.digital/tenant/api/v2/education/courses/01HQ5.../lessons/01HQ7.../complete \
  -H "Authorization: Bearer cativa_live_..."
```

Resposta ilustrativa do progresso (o schema autoritativo fica na **API Reference**, tag **Course**). `progressPercentage` deriva de `completedLessons / totalLessons`:

```json theme={null}
{
  "items": [
    {
      "userId": "01HQ0USER1234567890ABCDEF",
      "userName": "joao",
      "completedLessons": 6,
      "totalLessons": 10,
      "progressPercentage": 60.0
    }
  ],
  "total": 1
}
```

O aluno é sempre o usuário associado à credencial autenticada. Não envie `userId` no body. Se o usuário não tiver o badge exigido pelo curso, a matrícula retorna `403 forbidden`.

Quando todas as aulas estão concluídas, o curso é dado por concluído e o certificado é emitido. O aluno pode baixar o PDF do certificado do próprio curso:

```bash theme={null}
curl https://apis.cativalab.digital/tenant/api/v2/education/courses/01HQ5.../certificate/pdf \
  -H "Authorization: Bearer cativa_live_..." \
  --output certificado.pdf
```

## Certificados

Ao concluir o curso, um **certificado** é emitido para o aluno. Cada certificado carrega um código de verificação.

### Verificação pública por código (anônimo)

Este é o único endpoint da família que **não exige chave**. Ele confirma a autenticidade de um certificado pelo seu código, ideal para uma página pública de "validar certificado" onde qualquer pessoa cola o código e confere se é legítimo.

<CodeGroup>
  ```bash cURL theme={null}
  # Sem Authorization. Endpoint publico de verificacao de autenticidade.
  curl https://apis.cativalab.digital/tenant/api/v2/certificates/validate/ABCD-1234-EFGH
  ```

  ```js Node theme={null}
  // Chamada anonima: nenhum header de credencial.
  // Use numa landing page publica de "validar certificado".
  const res = await fetch(
    'https://apis.cativalab.digital/tenant/api/v2/certificates/validate/ABCD-1234-EFGH'
  );
  const result = await res.json();
  ```
</CodeGroup>

Resposta ilustrativa (schema completo na **API Reference**, tag **Certificate**). O `url` aponta o PDF do certificado, e `code` é o mesmo código validado:

```json theme={null}
{
  "url": "https://cdn.cativalab.digital/certificates/01HQ2CERT1234567890XYZ.pdf",
  "code": "ABCD-1234-EFGH",
  "certificateId": "01HQ2CERT1234567890XYZ",
  "userId": "01HQ0USER1234567890ABCDEF"
}
```

<Note>
  Como é anônimo, exponha esse fluxo direto no seu site sem passar a API Key para o navegador. O código é o único dado necessário.
</Note>

### Outras leituras de certificado

```bash theme={null}
# URL do certificado de um usuario especifico
curl https://apis.cativalab.digital/tenant/api/v2/certificates/url/01HQ9.../01HQ2... \
  -H "Authorization: Bearer cativa_live_..."

# Certificado associado a um curso
curl https://apis.cativalab.digital/tenant/api/v2/certificates/course/01HQ5... \
  -H "Authorization: Bearer cativa_live_..."
```

### Administração de certificados (admin)

O template e a emissão manual vivem sob `/admin/certificates`:

```
POST   /admin/certificates                       # criar template de certificado
GET    /admin/certificates                       # listar
GET    /admin/certificates/{certificateId}       # detalhe
PUT    /admin/certificates/{certificateId}       # editar
DELETE /admin/certificates/{certificateId}       # remover

POST   /admin/certificates/issue                 # emitir para um usuario
POST   /admin/certificates/issue-and-generate    # emitir e gerar o PDF
```

<Note>
  Não montamos aqui o schema de cada campo de resposta de certificado. Consulte a aba **API Reference** com a tag **Certificate** para os contratos exatos de emissão, template e validação.
</Note>

## Antipattern: um curso por cliente

<Warning>
  Não crie um curso por cliente que entra. Cursos são entidades estáticas de conteúdo, não containers por usuário. Para personalizar quem acessa, use **badges**: um único curso e o badge atribuído aos alunos certos. A matrícula e o certificado seguem o badge, não uma cópia do curso.
</Warning>

## Erros comuns e dúvidas

<AccordionGroup>
  <Accordion title="403 forbidden ao matricular o aluno">
    Falta o **badge** que o curso exige. Matrícula não é o mecanismo de acesso: o acesso é do badge. Atribua ao usuário o badge configurado no curso (tipicamente via compra/assinatura) e repita o `enroll`. Veja [Liberar acesso via compra](/pt-BR/guides/grant-access-via-purchase).
  </Accordion>

  <Accordion title="Matriculei o mesmo aluno duas vezes">
    A matrícula é idempotente por usuário e curso: matricular de novo não cria uma segunda matrícula nem zera o progresso. Pode dar retry com segurança em jobs de sincronização.
  </Accordion>

  <Accordion title="Todas as aulas concluídas mas o certificado não saiu">
    O certificado é emitido quando o progresso chega a 100% (todas as aulas com `complete`). Confira o `progress` do usuário: se `completedLessons` for menor que `totalLessons`, ainda falta marcar aula. Aulas adicionadas ao curso depois entram na conta e podem reabrir a pendência.
  </Accordion>

  <Accordion title="Preciso da API Key para validar um certificado no meu site?">
    Não. `GET /certificates/validate/{code}` é o único endpoint da família **sem chave**. Chame direto do navegador, na sua landing page de "validar certificado". Nunca coloque a API Key no frontend para os demais endpoints (esses são de servidor).
  </Accordion>

  <Accordion title="Devo criar um curso por cliente que compra?">
    Não. Curso é conteúdo estático, não um container por usuário. Um único curso serve todos; quem personaliza o acesso é o **badge** atribuído aos alunos certos. Um curso por cliente vira duplicata morta e quebra a matrícula.
  </Accordion>
</AccordionGroup>

## Próximos passos

<CardGroup cols={2}>
  <Card title="Badges como permissão" icon="shield-check" href="/pt-BR/concepts/badges-as-permissions">
    Como o acesso ao curso é liberado por badge, sem criar estrutura nova.
  </Card>

  <Card title="Liberar acesso via compra" icon="cart-shopping" href="/pt-BR/guides/grant-access-via-purchase">
    O fluxo típico: compra externa atribui o badge que matricula o aluno.
  </Card>

  <Card title="Eventos" icon="calendar" href="/pt-BR/concepts/events">
    Como eventos e lives se encaixam ao lado dos cursos na comunidade.
  </Card>
</CardGroup>
