Skip to main content
Um customer é a entidade que paga — um workspace, uma empresa ou, em B2C, o próprio contato. Ele também é um time, e esta página é toda forma de entrar em um. Customers vivem dentro de um ambiente, como todo registro do plano de clientes. Dados de teste e dados de produção compartilham o schema e nunca compartilham linhas — e um assento é uma linha.

Dois papéis, e nenhum terceiro

owner e member. Esse é o catálogo inteiro. Aqui embaixo não existe catálogo de permissões nem forma de definir um papel. Isso é deliberado: papéis e permissões existem um plano acima porque a sua organização define os dela, e um time B2B dentro do seu produto quer quem administra e todo o resto. Papéis customizados chegam quando alguém pedir. Dois invariantes vêm com os papéis, e os dois espelham o plano de staff:
  • Um customer nunca perde seu último owner. Rebaixá-lo, removê-lo ou ele sair responde 409 last_owner. Promova outra pessoa antes.
  • Você não pode trocar o seu próprio papel. É assim que alguém se dá tudo.

O customer ativo

Um contato pode pertencer a vários customers, então toda chamada diz dentro de qual ela está agindo — no header X-Customer-Id.
Trocar é navegação, não mutação. Nada é gravado quando alguém muda de time: você manda um header diferente. Três coisas seguem daí, e são a razão de não ser um active_customer_id guardado na sessão.
Um id de customer que alguém cole não casa com nenhuma linha de vínculo e responde 404 — o mesmo 404 de um customer que não existe. Revogar um vínculo vale na próxima requisição, não no próximo login.
Não existe um cursor compartilhado para uma aba mover debaixo da outra.
As claims seguem o que esta chamada pediu, e o próximo mint está a no máximo cinco minutos de distância.
Sem o header, o vínculo mais antigo do contato responde — a mesma queda que uma sessão de staff faz para a organização padrão. Um contato sem time nenhum não é um erro: é todo contato B2C. Leia a lista em GET /v1/contact/customers:

O time dentro do JWT

POST /v1/contact/token coloca o customer ativo no JWT de sessão, para o seu backend responder “qual time, e essa pessoa pode administrá-lo” offline:
Mande X-Customer-Id no mint e as claims seguem. Nomear um customer ao qual o contato não pertence é recusado com 404 em vez de simplesmente omitir a claim — um token sem o campo que o seu backend usa como porta é pior que um erro. As duas claims ficam ausentes quando o contato não pertence a customer nenhum. Trate ausente como “sem time”, nunca como “sem necessidade de checar”.

Abrindo um time

O contato que chamou vira o owner, para que a guarda do último owner tenha o que defender antes de qualquer outra pessoa chegar. Um visitante anônimo é recusado (identity_required). O seu próprio servidor também pode criar customers direto, que é o formato usual no modo federado — lá o seu produto já sabe quem trabalha com quem.

Convites

Só owner. Vale por 7 dias, um convite pendente por endereço por customer, e o e-mail sai em nome da sua organização — quem recebe é cliente do seu produto e pode nunca ter ouvido falar do UserKit.
A resposta não diz nada sobre o endereço já ter conta com você. O único conflito que ela reporta — already_member — é participação no time de quem chamou, que essa pessoa já lê na lista do time. Todo outro caso segue o caminho idêntico e produz a resposta idêntica.
Reenviar gera um token novo. POST /v1/contact/customer/invitations/{id}/resend rotaciona o hash e a validade, então o link da mensagem anterior para de funcionar: um convite é uma credencial, e nunca pode haver duas vivas para o mesmo assento. Revogar (DELETE …/{id}) fecha o assento de vez.

Aceitar exige duas provas

POST /v1/contact/invitations/accept precisa de um contato autenticado e do token. O token prova o convite — que este assento foi oferecido a este endereço. A sessão prova a pessoa. Nenhum dos dois basta sozinho: um link encaminhado sentaria quem o abrisse. Por isso o endereço convidado precisa ser uma identidade do contato que aceita, ou a chamada responde 403 invitation_not_yours. É a mesma regra que todo fluxo por endereço deste plano já segue: um e-mail é um atributo até algo transformá-lo em aresta de identidade, e um atributo é algo que qualquer um digita. Um endereço vira aresta pelo cadastro hospedado, por um magic link, por um código de seis dígitos, por um login social em que o provedor atestou o endereço, ou pelo seu próprio servidor chamando POST /v1/contacts com ele. Passar um endereço para /v1/boot no modo federado deliberadamente não faz isso.
Um convite pertence a um ambiente. Um contato do outro é recusado com o mesmo invalid_token de um link expirado — usado, expirado, revogado, nunca existiu e ambiente errado são uma resposta só, de propósito.

Eventos

Toda mutação publica um fato que você consome pelos eventos: Payloads carregam ids, nunca endereços — o e-mail fica fora de toda fila e todo log por onde o envelope passa.