Cria ou executa events
POST/api/events
- Auth
- Jeton requis
- Qui peut l’appeler:
- owner
- admin
- moderator
- Clé d’API:
- events:write
- X-CommunityId
X-CommunityIdEnvoyez le X-CommunityId de la communauté où l’opération a lieu.
Sujeito a verificação de papel (RBAC). Além disso, 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.
Na mesma transação nasce o produto do evento (`kind: event`, rascunho gratuito, preço `0`, 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}`. A capacidade (vagas) se define nesse produto com `capacity`. As listas e o detalhe de evento trazem o mesmo `product` (`capacity` e `available` só para a equipe; os demais veem `soldOut`); o detalhe acrescenta `access`, `canEnter` e `waitlist`. O produto do evento é o primeiro tipo de ingresso ("Ingresso", com um lote); `product` é o tipo padrão (o dos assinantes) e traz `ticketTypesCount`. Os tipos e lotes ficam em `GET /api/events/{id}/ticket-types`.
**Certificado:** o bloco opcional `certificate` `{ enabled, templateId?, text? }` (`EventCertificateInput`) liga o certificado para quem confirmar presença. Só owner e admin o enviam (moderador, 403). Ligar exige `endTime` (400). A resposta traz `data.certificate` (só para owner e admin) e `data.issuesCertificate`. Fim igual ou anterior ao início, 400.
**Rascunho (padrão, editor de eventos):** sem `publish`, o evento nasce `status: DRAFT`. Só `spaceId` é obrigatório; `title`, `startTime` e `slug` são opcionais (o servidor gera o slug do título, ou `event`, e o slug acompanha o título enquanto for rascunho). O rascunho **não dispara nada**: sem item no feed, sem aviso por e-mail, sem aviso em tempo real, sem moderação, sem pontos e sem o webhook `event.created`; só a equipe o vê. `data.emailNotice` vem `{ scheduled: false, reason: "draft" }` e `data.readiness` traz a lista "Para publicar" (`EventReadiness`). Publicar: `POST /api/events/{id}/publish`.
**`publish: true`** mantém o comportamento antigo (cria e publica na hora; `slug`, `title` e `startTime` obrigatórios): moderação do espaço, feed, `event.created` e, com `notifyByEmail` (padrão ligado) e quem cria sendo owner, admin ou moderador, o aviso por e-mail a quem pode ver o evento e tem a categoria "Eventos" ligada, 10 minutos depois (nunca para evento já passado; em moderação, só quando aprovado). A resposta traz `data.emailNotice` (`EmailNoticeResult`).
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 |
|---|---|---|
publishfacultatif | boolean | `true` cria e publica na hora (o comportamento de antes dos rascunhos; exige `slug`, `title` e `startTime`). Sem ele, nasce rascunho. |
spaceIdobligatoire | string (uuid) | O espaço do evento (obrigatório). |
slugfacultatif | string | Opcional no rascunho (gerado do título); obrigatório com `publish: true`. |
titlefacultatif | string | Opcional no rascunho; obrigatório com `publish: true`. |
startTimefacultatif | string | Início. Sem fuso (`2026-10-20T19:00:00`), é o horário da comunidade; com `Z` ou deslocamento (`2026-10-20T22:00:00Z`, `2026-10-20T19:00:00-03:00`), é esse instante. Obrigatório com `publish: true` (sem ele, 400 com `errors[].param = startTime`); no rascunho, opcional até publicar. |
endTimefacultatif | string | Fim, com a mesma regra de fuso do `startTime`. Tem de ser depois dele: igual ou antes, 400 com `errors[].param = endTime`. |
certificatefacultatif | object | Certificado de evento: bloco opcional de `POST` e `PUT /api/events`. Quem confirma presença (`going`) até o fim do evento recebe o certificado cerca de 1 hora depois. **Ausente** não mexe em nada: gravar outros campos do evento nunca muda o certificado, e evento 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ó eventos que terminam a partir dele emitem, sem emissão retroativa. Desligar mantém o modelo e o texto. **Só owner e admin** (e SuperAdmin) enviam o bloco: moderador e membro recebem 403, mesmo que possam editar o evento. Ligar exige `endTime` (400 sem ele, `errors[].param = certificate.enabled`). |
certificate.enabledfacultatif | boolean | Liga ou desliga. Ausente mantém o estado atual (desligado num evento 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 evento), `{data}` (a data do evento no fuso do evento, ou o da comunidade: "12 de outubro de 2026"; em dias diferentes "12 a 14 de outubro de 2026") e `{cargaHoraria}`; `[...]` entre colchetes some quando a variável dentro está vazia. Vazio ou `null` usa `certificate.defaultText`. · max 5000 |
checkInEnabledfacultatif | boolean | Usar check-in. Cada "Vou" (ou compra) tem um ingresso com QR e código (`GET /api/events/{id}/my-ticket`), a equipe faz o check-in na porta (`/api/events/{id}/check-in`) e, em evento online ou híbrido, abrir a transmissão ao vivo conta presença. Com ele ligado, o certificado automático do evento vai só para quem fez check-in. |
checkInBadgeIdfacultatif | string (uuid) | "Badge ao fazer check-in", já na criação (igual ao `PUT /api/events/{id}`): um badge do catálogo da comunidade, só owner e admin (moderador ou financeiro que o enviam, 403). Badge que não existe ou de outra comunidade, 422 no campo. A resposta traz `data.checkInBadge`. |
notifyByEmailfacultatif | boolean | Avisar por e-mail (regras em NotifyByEmailInput). Só vale com `publish: true`; no rascunho, vai no `POST /api/events/{id}/publish`. |
typefacultatif | online | hybrid | in_person | Type must be online, hybrid, or in_person |
locationfacultatif | string | Location must be a string |
placeIdfacultatif | string | Place ID must be a string |
streetfacultatif | string | Street must be a string |
numberfacultatif | string | Number must be a string |
neighborhoodfacultatif | string | Neighborhood must be a string |
cityfacultatif | string | City must be a string |
statefacultatif | string | State must be a string |
countryfacultatif | string | Country must be a string |
postalCodefacultatif | string | Postal code must be a string |
latitudefacultatif | number | Latitude must be a number |
longitudefacultatif | number | Longitude must be a number |
streamingUrlfacultatif | string (uri) | Streaming URL must be valid |
streamingServicefacultatif | youtube | vimeo | mux | Streaming service must be youtube, vimeo, or mux |
bannerUrlfacultatif | string (uri) | Banner URL must be valid |
featuredImageUrlfacultatif | string (uri) | Featured image URL must be valid |
videoTeaserUrlfacultatif | string (uri) | Video teaser URL must be valid |
hostIdsfacultatif | —[] | hostIds must be an array |
hostIds.*facultatif | string (uuid) | Each hostId must be a valid UUID |
organizerIdsfacultatif | —[] | organizerIds must be an array |
organizerIds.*facultatif | string (uuid) | Each organizerId must be a valid UUID |
timezonefacultatif | string | Fuso IANA do evento (ex.: America/Sao_Paulo). Opcional: sem ele, vale o fuso do país da comunidade. Os horários enviados são lidos nesse fuso. Validado como fuso IANA: `"Lua/Base"` → 400 com `errors[].param = timezone` (também no `PUT` e no `PATCH`). |
audiencefacultatif | string | Audience must be a string |
visibilityfacultatif | public | members | plan | private | `public` (qualquer pessoa), `members` (membros da comunidade), `plan` (**assinantes** de um plano, e a equipe) ou `private` (só a equipe: owner, admin, moderador, financeiro). O "Privado" das telas é `private`; `plan` abre o evento para quem assina. |
bannerFocalXfacultatif | number | Ponto focal da capa, horizontal (0 = esquerda, 1 = direita). `null` limpa. · ≥ 0, ≤ 1 |
bannerFocalYfacultatif | number | Ponto focal da capa, vertical (0 = topo, 1 = base). · ≥ 0, ≤ 1 |
featuredFocalXfacultatif | number | Ponto focal da imagem do card, horizontal. · ≥ 0, ≤ 1 |
featuredFocalYfacultatif | number | Ponto focal da imagem do card, vertical. · ≥ 0, ≤ 1 |
categoryfacultatif | livestream | workshop | webinar | meetup | demo | event | conference | festival | mentorship | panel | masterclass | bootcamp | networking | round_table | pitch | Tipo do evento. Fora da lista: 400 com `errors` no campo `category` (antes era 500). |
Réponses
| Statut | Description |
|---|---|
201 | Evento criado (rascunho, ou publicado com `publish: true`). Traz `status`, `publishedAt`, `updatedAt` e os pontos focais; `data.certificate` só para owner e admin. |
400 | Requisição inválida — falha de validação. |
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. |
404 | Recurso não encontrado. |
500 | Erro interno do servidor. |
Exemple curl
curl -X POST "https://api.memberfy.net/api/events" \
-H "Authorization: Bearer $TOKEN" \
-H "X-CommunityId: $COMMUNITY_ID" \
-H "Content-Type: application/json" \
-d '{"spaceId":"00000000-0000-0000-0000-000000000000"}'