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

# Segmentos

> Uma audiência declarativa, reusada por todo módulo que precisa escolher pessoas — e as condições que ela se recusa a aproximar.

Um segmento é uma resposta salva para "quem são essas pessoas". Flags apontam para
um, checklists são mostradas para um, posts de changelog são endereçados a um,
pesquisas perguntam a um. Esse reuso é o ponto inteiro: um módulo que criasse o
próprio filtro seria um segundo vocabulário para *audiência*, e os dois
discordariam na primeira vez que um campo fosse adicionado a um deles.

## O formato

Uma conjunção de grupos; dentro de um grupo, uma disjunção de condições.

```json theme={null}
{
  "name": "Verificados, quietos há um mês",
  "definition": {
    "groups": [
      { "conditions": [{ "source": "attribute", "field": "email_verified", "operator": "is_true" }] },
      { "conditions": [{ "source": "event", "operator": "not_occurred", "value": "checkout.paid", "days": 30 }] }
    ]
  }
}
```

`(A OU B) E C`, e **dois níveis são a gramática inteira**. Aninhamento arbitrário é
uma questão de precedência, um parser e um formulário que ninguém consegue
desenhar; qualquer-um E qualquer-um é o que um construtor de audiência de fato
oferece, e este diz em voz alta o que não consegue expressar em vez de suportar
pela metade.

No máximo **8 grupos de 12 condições**. Isso é uma audiência grande e uma query que
o Postgres planeja sem notar — o limite existe para que uma definição não possa
ficar arbitrariamente cara de colar.

Um **grupo vazio é recusado**, nunca lido como "sempre verdadeiro": um construtor
que perdesse a última condição viraria silenciosamente "todo mundo".

## Quatro fontes

| Fonte       | O que lê                                                                                                                  | Campos                                                                                                                                                                                      |
| ----------- | ------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `attribute` | A linha do contato — o perfil, as marcas de verificação, a atribuição de primeiro toque                                   | `email`, `name`, `email_verified`, `identified`, `created_at`, `first_seen_at`, `last_seen_at`, `identified_at`, `email_verified_at`, `first_utm_*`, `first_referrer`, `first_landing_page` |
| `plan`      | O espelho de assinaturas                                                                                                  | `key`, `subscription_status`                                                                                                                                                                |
| `event`     | O que aconteceu no seu produto — suas chamadas de [`track()`](/pt-br/guides/analytics) e os fatos `$auth.*` da plataforma | o nome do evento vai em `value`                                                                                                                                                             |
| `metric`    | O medidor de atividade                                                                                                    | `active_months`                                                                                                                                                                             |

Quais operadores se aplicam é decidido pelo campo, e uma incompatibilidade é
**recusada**: `created_at contains "@"` não é uma query com resposta vazia, é uma
pergunta que ninguém quis fazer. Campos de texto aceitam `eq`/`neq`/`contains`/
`not_contains`/`starts_with`/`ends_with`/`in`/`not_in`/`is_set`/`is_not_set`;
booleanos aceitam `is_true`/`is_false`; timestamps aceitam `before`/`after`/
`within_days`/`not_within_days`; eventos aceitam `occurred`/`not_occurred`;
métricas aceitam `at_least`/`at_most`.

Um operando que a condição não lê é recusado em vez de ignorado. Um `days` numa
condição onde dias não significam nada seria uma janela que você definiu, o motor
descartou, e você acredita ter.

A **janela de uma condição de evento é obrigatória** (1 a 730 dias). Não existe
`occurred` sem limite, porque um segmento que varre todo evento já armazenado fica
mais lento a cada mês em que é deixado em paz. Se você quer dizer "alguma vez",
diga dois anos e assuma isso.

Um timestamp **não preenchido** não casa com nenhum dos quatro operadores de tempo
— `not_within_days` inclusive. Um valor ausente não responde pergunta nenhuma sobre
si mesmo; alcance esses contatos com `is_not_set`.

## Filtrando por uma propriedade do evento

Uma condição de evento pode olhar **uma chave** do que o `track()` mandou:

```json theme={null}
{
  "source": "event", "operator": "occurred",
  "value": "checkout.completed", "days": 30,
  "property": "plan", "property_operator": "eq", "property_value": "pro"
}
```

Sem isso o caminho que sobra é codificar a dimensão no nome —
`checkout.completed.pro` ao lado de `checkout.completed.free` — o que transforma o
namespace num produto cartesiano e leva junto o agrupamento em que o explorer é
construído.

Quatro regras, e cada uma existe por um motivo que aparece depois:

* **Só em `occurred`.** "Não aconteceu com plan=pro" tem duas leituras — nunca
  aconteceu, ou aconteceu com outro plano — e o motor recusa em vez de escolher.
  Para a primeira, use `not_occurred` no evento inteiro.
* **Uma chave por condição.** Duas seriam um E, e um grupo de condições já é como
  este motor escreve E.
* **A comparação é textual.** Uma propriedade é comparada como o texto que ela
  vira: `4` é `"4"`, `true` é `"true"`. A coluna não tem schema, e qualquer outra
  leitura precisaria de um tipo declarado por chave. Os operadores são os de
  texto, menos `in`/`not_in` — que sobre uma propriedade seriam um OU, e um OU é
  uma segunda condição no mesmo grupo.
* **Ausente e `null` são o mesmo estado que vazio.** Então `is_not_set` é como
  você pede os eventos que não carregam a chave, e `neq` não casa com eles por
  acidente.

O `count` continua valendo junto: "fez checkout com `plan = pro` ao menos 2 vezes"
é uma condição só.

Uma propriedade de evento **não** é um atributo do contato. Se você escrever
`properties.plan` como `attribute`, a recusa aponta para cá.

## O que ele recusa, e por que recusar é melhor que aproximar

Duas condições são perfeitamente razoáveis de querer, e este motor não vai
formulá-las. As duas se nomeiam no erro, então você encontra uma frase em vez de
uma audiência vazia.

**Um atributo customizado do contato.** `attributes.plan_tier` não tem nada por
trás — as colunas do contato são o perfil e a atribuição de primeiro toque, e não
há armazém de atributos contra o qual casar. O único jeito de honrar isso seria
casar algo *quase* certo. Um segmento construído sobre uma aproximação não é um
erro menor que uma mensagem de erro: é uma audiência que recebe e-mail, ou uma
funcionalidade que sai para o décimo errado dos seus usuários, e ninguém descobre
isso lendo a definição.

**Um entitlement.** Se um cliente *detém* uma funcionalidade é resolvido pelo
[motor de entitlements](/pt-br/guides/entitlements) — overrides que substituem em
vez de maximizar, uma expiração, uma carência de dunning, e um vocabulário de
status cujo membro desconhecido responde honestamente "não dá para dizer".
Reescrever isso em SQL seria uma segunda cópia da única função que é mantida
sozinha de propósito, e a cópia estaria errada justamente nos casos que mais
importam. O que um segmento *pode* mirar é o **plano**, que é uma linha:
`plan.key` e `plan.subscription_status` são espelhos do seu gateway, não resoluções
de coisa alguma.

Uma terceira recusa é mais estreita e vale saber: uma condição `metric` num
ambiente **test**. O medidor de atividade nunca registra test, então a condição só
poderia casar com ninguém — e uma audiência vazia por razão estrutural é idêntica
a uma audiência vazia porque ainda não aconteceu nada.

## Dois avaliadores, um significado

A mesma definição é compilada duas vezes: em SQL sobre os contatos de um ambiente
("quem está aqui"), e numa avaliação em processo de um contato ("essa pessoa está
aqui, agora"). Uma campanha resolvendo destinatários quer o primeiro; uma flag
decidindo o que mandar para um navegador no `boot`, ou uma pesquisa perguntando se
alguém *acabou de entrar*, quer o segundo.

Duas implementações de um significado é exatamente o formato que se descola, então
cada campo carrega as duas metades num só lugar e um teste conduz contatos reais
pelos dois caminhos e recusa uma discordância. Vale saber que isso existe, porque é
a razão de você poder confiar no preview: a contagem que você viu é produzida pelas
mesmas regras que a flag vai aplicar um carregamento de página depois.

## Pertencimento, entrada e saída

Quem está num segmento é **escrito**, não recalculado a cada leitura — porque o que
os módulos acima de fato assinam é a *entrada*. "Alguém entrou em trial-acaba-em-3-dias"
é uma diferença entre duas avaliações, e uma diferença não tem onde morar a menos
que a avaliação anterior tenha sido registrada.

Duas coisas mantêm isso vivo:

* **Um fato chega** e os contatos sobre os quais ele fala são re-decididos em
  segundos. É o caminho que carrega tudo causado por alguém ter feito algo.
* **Uma varredura noturna** percorre cada definição viva e recalcula. É a única
  coisa capaz de notar uma condição que virou verdadeira porque *o tempo passou* —
  "criado há mais de 30 dias", "não visto há três semanas" viram verdade sem nada
  ser publicado em lugar nenhum, porque nada aconteceu.

Entrar publica [`segment.entered`](/pt-br/guides/webhooks) e sair publica
`segment.left`. Os dois são entregáveis como webhook, e o payload leva os ids e o
nome do segmento — nunca a definição.

**O primeiro cálculo de uma definição não anuncia nada.** Escrever uma audiência
pela primeira vez é um backfill: ninguém mudou, a audiência mudou, e um webhook por
contato existente por um ato que você fez no painel três segundos atrás não é um
fato sobre ninguém. Editar a definição devolve o segmento a esse estado, pelo mesmo
motivo — re-mirar uma audiência não é cem pessoas entrando nela.
`definition_version` sobe quando a definição muda e nunca num rename, então
qualquer coisa segurando uma audiência compilada descobre que está velha comparando
um inteiro.

`left_at` é um timestamp e não uma exclusão, porque sair também é algo sobre o qual
você vai querer agir: uma audiência de reconquista é gente que *saiu* de "ativo nos
últimos 30 dias".

## Faça o preview antes de mirar qualquer coisa

```http theme={null}
POST /v1/organization/segments/{id}/preview
```

A contagem e no máximo 25 contatos, do mais novo para o mais velho. Não escreve nada
e custa uma query. O hábito que vale criar é dar preview antes de uma flag, uma
pesquisa ou um post de changelog nomear o segmento — todo módulo a jusante herda o
que essa definição significa, e o lugar mais barato de descobrir que ela significa
outra coisa é aqui.

## Permissões

Ler um segmento é `customers:read` — o mesmo assento que lista contatos, porque um
preview *é* ler contatos. Criar, editar e arquivar é `segments:manage`, que owner e
admin têm por padrão.

A separação é deliberada. Uma audiência é configuração de produto que todo outro
módulo herda; poder olhar para uma não é o mesmo ato que poder mover aquilo para
onde uma flag, uma pesquisa e uma nota de versão apontam ao mesmo tempo.
