Pular para o conteúdo

Escopo ​

As rotas de admin de In-App ficam em /v1/apps/{app_id}/in-app-messages e aceitam uma sessão de admin ou uma chave REST (Authorization: Bearer rk_...), de modo que um backend ou um script pode gerenciar mensagens sem o painel. Toda rota tem o escopo do App no caminho. Os formatos de requisição e resposta campo a campo estão na Referência da API de In App gerada.

Dispositivos não usam essas rotas. As rotas voltadas ao SDK (eligible e events) usam uma chave de cliente e estão descritas no Contrato de mensagens in-app.

Criar e ativar ​

POST /v1/apps/{app_id}/in-app-messages cria um rascunho por padrão, ou uma mensagem ativa quando o corpo tem "status": "active":

bash
curl -X POST https://your-instance/v1/apps/{app_id}/in-app-messages \
  -H "Authorization: Bearer $REST_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Example announcement",
    "format": "top",
    "title": "Example title",
    "body": "Example body.",
    "buttons": [
      { "label": "Open", "action": { "type": "deeplink", "value": "exampleapp://offers/42" } }
    ],
    "platforms": ["ios", "android"],
    "frequency": { "type": "once" }
  }'
  • Campos desconhecidos são rejeitados. format é o layout: top, center (padrão no painel), bottom ou full. frequency é once, n_times (com max) ou interval (com interval_seconds).
  • A imagem é media_id (um upload da Media API) ou uma image_url via https, nunca ambos.
  • Os ids dos botões são atribuídos pelo servidor e permanecem estáveis entre edições.
  • Links aceitam https e esquemas customizados de app; http, javascript, data, file, vbscript, blob e about são rejeitados.
  • Uma ação de botão é url, deeplink ou push_permission. push_permission pede ao dispositivo a permissão de notificação, não recebe value (um valor resulta em 422 invalid_content, com buttons[0].action.value: invalid_action_value em details.fields para o primeiro botão) e não é aceita em image_action. O servidor oculta uma mensagem com essa ação dos dispositivos que já recebem push e dos dispositivos em um SDK que ainda não a suporta; veja o contrato.
  • Um corpo rejeitado resulta em 422 invalid_content com details.fields mapeando cada campo a um código.

PATCH .../{id} atualiza o conteúdo (uma chave omitida mantém seu valor, null limpa media_id, image_url, starts_at, ends_at, style, image_action e dismiss_after_seconds; buttons, platforms, segment_ids, exclude_segment_ids, frequency, style, image_action e trigger são substituídos por inteiro, e null ou [] esvazia as duas listas de segmentos). Mudanças de status passam por POST .../{id}/activate e POST .../{id}/pause. Edições em uma mensagem ativa chegam aos dispositivos no próximo fetch. DELETE .../{id} faz soft-delete da mensagem: ela sai das listas e dos dispositivos, suas métricas continuam legíveis por id, e os segmentos que ela usava podem ser excluídos novamente.

Aparência, comportamento e trigger ​

Quatro campos opcionais definem como o SDK exibe a mensagem. Eles fazem parte do mesmo corpo de criação e de PATCH:

json
{
  "format": "center",
  "style": {
    "background_color": "#102030",
    "text_color": "#FFFFFF",
    "button_color": "#E94B8B",
    "button_text_color": "#FFFFFF",
    "text_align": "center",
    "margin": true
  },
  "image_url": "https://cdn.example.com/offer.png",
  "image_action": { "type": "url", "value": "https://example.com/offer" },
  "dismiss_after_seconds": 8,
  "trigger": { "type": "event", "name": "checkout_done" }
}
  • style contém até cinco tokens (background_color, text_color, button_color, button_text_color como #RRGGBB; text_align como left, center ou right) e margin. O conjunto é fechado: uma chave desconhecida resulta em 422 invalid_content com style.<key>: unknown. Uma cor que não seja #RRGGBB resulta em style.<key>: invalid_color, um alinhamento fora dos três valores resulta em style.text_align: invalid_align. As cores são armazenadas em maiúsculas. style é null quando não definido, e um PATCH que envia um objeto substitui o style inteiro, então um token omitido é limpo.
  • image_action torna a imagem tocável. Ela tem o formato e as regras de link de uma ação de botão e exige uma imagem (image_action: requires_image caso contrário). Limpe a ação junto com a imagem.
  • dismiss_after_seconds fecha a mensagem após 1 a 300 segundos em tela (out_of_range caso contrário). null a mantém até o usuário fechá-la.
  • trigger é {"type": "session_start"} (o padrão, e o valor que a API retorna quando nenhum é definido) ou {"type": "event", "name": "..."} com um nome de 1 a 64 caracteres entre a-z, 0-9, _, . e -. Códigos: trigger: invalid_type, trigger.name: required, invalid_name, too_long, e not_allowed para um nome em um trigger session_start. "trigger": null em um PATCH define session start.

GET /v1/apps/{app_id}/in-app-messages/trigger-events retorna { "events": ["checkout_done", "..."] }: os nomes de evento distintos usados pelas mensagens não excluídas do App, ordenados, no máximo 50. Os nomes de evento não são registrados em nenhum outro lugar; a lista só alimenta sugestões.

Como o SDK usa esses campos está no Contrato de mensagens in-app.

Segmentação com segmentos ​

Uma mensagem segmenta os mesmos segmentos que você usa para push. segment_ids lista os segmentos para os quais ela é exibida e exclude_segment_ids lista aqueles cujos dispositivos nunca a veem:

  • Um dispositivo vê a mensagem quando corresponde a pelo menos um segmento em segment_ids, ou quando essa lista está vazia (todos os dispositivos), e não corresponde a nenhum dos segmentos em exclude_segment_ids.
  • Cada lista comporta até 10 segmentos. Um segmento não pode se repetir nem aparecer nas duas listas. Os campos resultam em 422 invalid_content com os códigos invalid_id, duplicate_id, included_and_excluded ou too_many.
  • Um segmento de outro App, ou que não existe, resulta em 404 segment_not_found.
  • Um segmento cujas regras armazenadas não são mais válidas nunca corresponde. Uma mensagem que o inclui não é exibida, e uma mensagem que o exclui também não é exibida, de modo que uma exclusão ilegível nunca amplia a audiência.
  • Um segmento usado por uma mensagem não excluída não pode ser excluído (409 segment_in_use).

Limite do plano ​

O plano limita quantas mensagens podem estar ativas ao mesmo tempo por App. Ativar além do limite, por activate ou criando uma mensagem ativa, resulta em:

json
{ "error": { "code": "in_app_limit_exceeded", "message": "...", "details": { "reason": "in_app_limit_exceeded" } } }

com status 402. Uma assinatura inativa resulta em 402 subscription_inactive. Rascunhos e mensagens pausadas não ocupam uma vaga, e uma mensagem após seu ends_at deixa de contar.

Mudar para um plano que permite menos mensagens ativas do que um App tem no momento não é rejeitado. Quando a mudança entra em vigor, o Notti pausa as mensagens acima do novo limite, mantendo ativas as mais antigas (por data de criação). A pausa é aplicada por uma verificação em segundo plano que roda a cada cerca de 5 minutos, então pode haver uma pequena defasagem em relação à mudança de plano. Mensagens pausadas mantêm conteúdo e métricas, e você pode reativá-las enquanto o App estiver abaixo do limite.

Listar e filtrar ​

GET /v1/apps/{app_id}/in-app-messages retorna as mensagens da mais nova para a mais antiga, com paginação por cursor (limit de 1 a 100, cursor vindo de next_cursor).

  • status filtra pelo status de exibição: all (padrão), draft, active, scheduled, paused ou completed. scheduled é uma mensagem ativa cujo starts_at ainda está no futuro, e completed é uma mensagem ativa cujo ends_at já passou. O campo status armazenado é apenas draft, active ou paused; cada item também traz display_status.
  • search busca no nome da mensagem (até 200 caracteres).
  • Cada item tem impressions, reach e ctr de todo o período, e a resposta tem active_count e active_limit (null quando não há limite).

Estimativa de audiência ​

POST /v1/apps/{app_id}/in-app-messages/audience-estimate com { "segment_ids": ["..."], "exclude_segment_ids": ["..."], "platforms": ["ios"] } retorna { "reach": N }: quantos dispositivos do App a audiência corresponde agora, com as mesmas regras de inclusão e exclusão acima. Deixe segment_ids vazio para todos os dispositivos e platforms vazio para todas as plataformas. Os dispositivos contam permitindo push ou não.

Métricas ​

GET /v1/apps/{app_id}/in-app-messages/{id}/metrics?range=14d retorna totais, uma série diária preenchida com zeros, cliques por botão (uma linha com button_id: "image" vem primeiro quando a mensagem tem uma ação de imagem) e detalhamentos. totals traz dismissers, os dispositivos que fecharam a mensagem por conta própria, e auto_dismissed, os dispositivos que apenas a viram fechar pelo timer. range é 7d, 14d (padrão), 30d, 90d, ou custom com from e to (YYYY-MM-DD, to inclusivo). Mensagens excluídas, pausadas e encerradas continuam legíveis. As definições das métricas estão em O que as métricas significam.