SuperdindaDocs

Onboarding

Da conta de família à vaga ativa, com o consentimento antes do cartão (D2) e o teste grátis contado a partir da primeira conexão (D1).

O onboarding é por conta de família. Nada guarda "em qual tela a pessoa está": GET /accounts/{uid}/onboarding deriva o próximo passo dos dados a cada chamada.

Os passos

add_seat ─► fill_dependent ─► send_consent ─► await_consent ─► activate_seat ─► (null: completo)
                                   ▲               │
                                   │               ├─ a pessoa recusou ─► consent_declined
                                   └───────────────┴─ link venceu / foi retirado / dados mudaram
PassoChamada que avança
(antes de tudo)POST /accounts com { name }. Quem cria vira owner. GET /accounts vazio quer dizer que o onboarding começa aqui
add_seatPOST /accounts/{uid}/seats, sem corpo. Cria a vaga pending com o preço de hoje
fill_dependentPUT /seats/{uid}/dependent com kind (minor ou adult), name, birthDate, relationship, phone (E.164, o WhatsApp que será pareado) e document ({ type, number }, CPF validado)
send_consentPOST /seats/{uid}/consent-request com { send: "now" } ou { send: "later" } (só owner)
await_consentA pessoa responde na página pública (veja Consentimento). SSE consent.given
consent_declinedReenviar depois da espera (POST /seats/{uid}/consent-request/resend) ou arquivar a vaga (DELETE /seats/{uid})
activate_seatPOST /seats/{uid}/activate com { guardianAttestation: true, campaignCode? } (só owner)

Como o passo de cada vaga pendente é derivado (seatStep):

  1. Sem pessoa, ou sem telefone ou documento: fill_dependent.
  2. Vaga antiga (activationRequiresConsent: false): activate_seat, na ordem antiga.
  3. Pelo status do consentimento: given dá activate_seat, pending dá await_consent, declined dá consent_declined, e nenhum, expired ou revoked dão send_consent.

No topo, nextStep é o passo da vaga pendente mais antiga (nextSeatUid aponta qual), ou add_seat se não há vaga pendente. É null quando alguma vaga está ativa (complete: true).

Detalhes que mudam o fluxo

  • Consentimento antes do cartão (D2). Vagas criadas desde a migração do onboarding v2 têm activationRequiresConsent: true, e a ativação responde 422 CONSENT_REQUIRED até o sim (ou 422 RELATIONSHIP_REFUTED). Leia a flag em vez de supor a ordem.
  • send: "later" cria o pedido e o link (seat.consent.link) sem enviar nada: o responsável copia o link ou chama de novo com "now". O pedido aberto é reaproveitado, então "later" seguido de "now" envia o mesmo link. Pra um link novo, use o resend. A checagem de vínculo roda uma vez por vaga, nesta chamada.
  • Dados da pessoa. Reenviar os mesmos dados não muda nada. Mudar os dados com o link aberto retira o pedido (consent.revoked, volta a send_consent). Depois do sim, os dados travam (422 DEPENDENT_LOCKED): pra mudar, arquive a vaga pendente e crie outra.
  • Um "não" antes da ativação deixa a vaga pending. Nada é cobrado e a assinatura não é aberta.
  • Ativação. Grava a atestação do responsável, abre a assinatura em trialing se a conta não tem uma e ativa a vaga. Em vagas antigas, também manda o link de consentimento e começa a checagem de vínculo.
  • Teste grátis (D1). Não começa na ativação: trial.state fica not_started até a primeira conexão de WhatsApp da vaga chegar a paired. Aí sai o SSE trial.started. O teste é por vaga, e o da conta é o da primeira vaga que conectou. Use trial.label (ou catalog.trialLabel antes de existir conta) pro texto, e nunca deduza "vencido" de um trialEndsAt nulo.
  • POST /accounts/{uid}/subscription abre a assinatura explicitamente (pra escolher intervalo ou campanha). Não faz parte do onboarding.
  • GET /catalog é público e devolve os serviços com o preço que a pessoa pagaria, mais trialDays, trialStartsOn e trialLabel.

Erros comuns

codeOndeO que fazer
DEPENDENT_REQUIRED / DEPENDENT_INCOMPLETEconsent-request, activateVoltar a fill_dependent
SEAT_NOT_PENDINGconsent-request, activateVaga ativa: usar o resend
CONSENT_ALREADY_GIVENconsent-requestIr pra ativação
RESEND_LOCKEDconsent-request, resendEsperar details.retryAt
CONSENT_REQUIRED / RELATIONSHIP_REFUTEDactivateVoltar a await_consent / mostrar o bloqueio
DEPENDENT_LOCKEDdependentOferecer arquivar e recomeçar
CAMPAIGN_NOT_FOUND / CAMPAIGN_ALREADY_APPLIEDactivateTirar o código de campanha
BIRTH_DATE_OUT_OF_RANGEdependentminor precisa ter menos de 18 anos
Do zero até esperar o sim
sd() { curl -s -H "authorization: Bearer $TOKEN" -H 'content-type: application/json' "$@"; }

sd -X POST "$API/accounts" -d '{"name":"Família Silva"}'      # → data.uid (conta)
sd -X POST "$API/accounts/$ACCOUNT/seats"                      # → data.uid (vaga)
sd -X PUT  "$API/seats/$SEAT/dependent" -d '{
  "kind": "minor", "name": "Lia", "birthDate": "2014-03-09", "relationship": "child",
  "phone": "+55<telefone>", "document": { "type": "cpf", "number": "<cpf>" } }'
sd -X POST "$API/seats/$SEAT/consent-request" -d '{"send":"now"}'
sd "$API/accounts/$ACCOUNT/onboarding"                         # → nextStep: "await_consent"

Em aberto

  • A atestação do responsável é gravada na ativação, depois que o link já foi pra pessoa menor de idade. Falta confirmar isso com o jurídico (LGPD, art. 14), como anotado na frente k-onboarding.
  • Se alguém dá o sim e nunca conecta, não há teste nem período. Como cobrar essa vaga fica pro G-33/G-34 do api-gaps.md.

Fontes

  • backend/src/http/routes/accounts/create.route.ts, list.route.ts, add-seat.route.ts, onboarding.route.ts, subscribe.route.ts, schemas.ts
  • backend/src/http/routes/seats/dependent.route.ts, consent-request.route.ts, activate.route.ts, archive.route.ts
  • backend/src/domains/subscriptions/use-cases/onboarding-status.ts, activate-seat.ts, set-dependent.ts, start-trial.ts
  • backend/src/domains/subscriptions/schemas/dependent.schema.ts, activate.schema.ts
  • backend/src/http/routes/catalog/list.route.ts
  • Notas da frente k-onboarding (k-onboarding-api.md)
  • AD-006, AD-009, AD-019 (D1, D2)

On this page