Cria um curso
POST/api/courses
- Auth
- Jeton requis
- Qui peut l’appeler:
- owner
- admin
- Clé d’API:
- courses:write
- X-CommunityId
X-CommunityIdEnvoyez le X-CommunityId de la communauté où l’opération a lieu.
Só owner e admin. O curso **sempre nasce em rascunho** (`status: draft`), seja qual for o corpo. Na mesma transação nasce o produto do curso (`kind: course`, rascunho gratuito, preço `0`, mesma imagem e título, moeda da comunidade); a resposta traz `data.product` (`{ id, kind, status, price, currency, capacity, available, soldOut }`) para o botão "Preço e venda" abrir `PUT /api/products/{id}`. Falha em um desfaz o outro. `GET /api/courses`, `GET /api/courses/{id}` e a lista de cursos acessíveis trazem o mesmo `product` (`capacity`/`available` só para a equipe; os demais veem `soldOut`); o detalhe acrescenta `access` (`team`, `open`, `purchase`, `plan`, `grandfathered`, `paid`), `canEnter` e `waitlist` (o lugar da própria pessoa na fila de espera, ou `null`); a equipe monta módulos e aulas e depois publica com `PUT /api/courses/{id}`. Respeita o `canCreate` do space: `staff` aceita owner, admin, moderator e finance; `admins` aceita owner e admin; `anyone` não restringe. Fora da regra, 403 com `errors[0].param = "spaceId"`. SuperAdmin sempre passa. O curso nasce na comunidade do `X-CommunityId`; `communityId` no corpo é opcional e, se vier diferente do header, 403 com `param = communityId`.
**Erros no campo (400, `errors[].param`):** `title` vazio ou só espaços, `level` fora de `beginner`, `intermediate`, `advanced`, `slug` inválido ou **já usado** por outro curso da comunidade, `spaceId` ausente ou de um espaço que não é desta comunidade.
**Espaço em modo "Um curso só" (409):** se o espaço (`Space.moduleConfig.mode = "single"`) já tem um curso, a criação é recusada com `errors[0].param = "spaceId"` e a mensagem orienta a editar o curso existente ou trocar o espaço para "Lista de cursos". Conta todo curso não excluído, em qualquer status (rascunho, publicado ou arquivado). A conferência roda numa transação com trava na linha do espaço: de N criações simultâneas, uma passa e as outras N-1 recebem 409. Espaço em "Lista de cursos" não tem limite.
**Certificado:** o bloco opcional `certificate` `{ enabled, templateId?, text? }` liga o certificado na conclusão do curso (ver `CourseCertificateInput`). Sem `templateId`, vale o modelo padrão do sistema, criado na primeira vez (e o espaço "Certificados", se a comunidade não tem um). Erros em `errors[].param`: `certificate.templateId` (modelo de outra comunidade, 404; inativo, 400), `certificate.text` (HTML ou acima de 5000 caracteres), `certificate.enabled`.
En-têtes
| Nom | Type | Description |
|---|---|---|
X-CommunityIdfacultatif | string (uuid) | ID da comunidade em que a operação acontece. Obrigatório na maioria dos endpoints com escopo de comunidade. |
Corps de la requête application/json
| Nom | Type | Description |
|---|---|---|
communityIdfacultatif | string (uuid) | Opcional; se vier, tem de ser o do `X-CommunityId`. |
spaceIdobligatoire | string (uuid) | Espaço de Cursos onde o curso nasce. |
slugobligatoire | string | Minúsculas, números e hífen; único na comunidade. |
titleobligatoire | string | max 255 |
levelobligatoire | beginner | intermediate | advanced | |
descriptionfacultatif | string | |
bannerUrlfacultatif | string | URL http ou https, até 2048 caracteres; outro esquema (`javascript:`, `data:`) ou texto longo, 400 em `bannerUrl`. Vazio ou `null` limpa. · max 2048 |
coverFormatfacultatif | BANNER | POSTER | VIDEO | Proporção da capa: `BANNER` (3:1, padrão), `POSTER` (2:3) ou `VIDEO` (16:9). Opcional; ausente, não muda (na criação, `BANNER`). Outro valor, 400 em `coverFormat`. |
isFeaturedfacultatif | boolean | |
tagsJsonfacultatif | string[] | |
requiredPlanTierfacultatif | string | Guardado, mas ignorado: o acesso é o do espaço. |
requireSequentialfacultatif | boolean | Aulas em sequência. Padrão `false`. Valor que não seja booleano, 400. |
certificateMinGradefacultatif | integer | Nota final mínima para o certificado de conclusão. Fora de 0 a 100, 400. · ≥ 0, ≤ 100 |
completionBadgeIdfacultatif | string (uuid) | "Badge ao concluir", já na criação (igual ao `PUT /api/courses/{id}`): um badge do catálogo da comunidade; quem conclui o curso recebe o badge. Badge que não existe ou de outra comunidade, 422 no campo. A resposta traz `data.completionBadge`. |
instructorMemberIdfacultatif | string (uuid) | Ver `CourseCommunityFieldsInput`. |
learningPointsfacultatif | string[] | Ver `CourseCommunityFieldsInput`. |
leaderboardEnabledfacultatif | boolean | Ver `CourseCommunityFieldsInput`. |
certificatefacultatif | object | Certificado na conclusão do curso. Bloco opcional de `POST` e `PUT /api/courses` (e de `course` em `POST /api/spaces`, no modo "Um curso só"). **Ausente** não mexe em nada: gravar outros campos do curso nunca muda o certificado, e curso novo sem o bloco nasce desligado. `certificateEnabledAt` é do servidor e **não** é enviado: ele grava na virada de desligado para ligado (religar grava de novo) e zera ao desligar; só quem conclui o curso a partir dele tem direito, sem emissão retroativa. Desligar mantém o modelo e o texto. Só owner e admin gravam. |
certificate.enabledfacultatif | boolean | Liga ou desliga. Ausente mantém o estado atual (desligado num curso novo). |
certificate.templateIdfacultatif | string (uuid) | Modelo do módulo Certificados. Tem de ser ATIVO e da mesma comunidade: de outra comunidade, 404; rascunho, arquivado ou apagado, 400 (`errors[].param = certificate.templateId`). Ausente mantém; `null` ou sem modelo vale o padrão do sistema (criado sob demanda, com o espaço Certificados se a comunidade não tem um). |
certificate.textfacultatif | string | Texto do certificado: texto simples (sem HTML), com as variáveis `{nome}`, `{assunto}` (o título do curso) e `{cargaHoraria}`; `[...]` entre colchetes some quando a variável dentro está vazia. Vazio ou `null` usa `config.defaultText`. · max 5000 |
notifyByEmailfacultatif | boolean | Avisar por e-mail ao publicar (regras em NotifyByEmailInput). |
Réponses
| Statut | Description |
|---|---|
201 | Curso criado em rascunho. |
400 | Campo inválido ou slug repetido (`errors[].param` aponta o campo). |
401 | Token ausente, inválido ou expirado; token de outra comunidade (o `X-CommunityId` ou o `communityId` do corpo não é o do token); token antigo ou de uma sessão encerrada (troca de senha, convite aceito). Nesses casos a mensagem pede para entrar de novo (`auth.signInAgain`). |
403 | Autenticado, mas sem permissão para esta operação. |
409 | Espaço em "Um curso só" que já tem um curso. |
500 | Erro interno do servidor. |
Exemple curl
curl -X POST "https://api.memberfy.net/api/courses" \
-H "Authorization: Bearer $TOKEN" \
-H "X-CommunityId: $COMMUNITY_ID" \
-H "Content-Type: application/json" \
-d '{"spaceId":"00000000-0000-0000-0000-000000000000","slug":"string","title":"string","level":"beginner"}'