risetechapps / notify-service-for-laravel
Package info
github.com/risetechapps/notify-service-for-laravel
pkg:composer/risetechapps/notify-service-for-laravel
Requires
- php: ^8.3
- illuminate/support: ^12
Requires (Dev)
- fakerphp/faker: ^1.23
- laravel/pail: ^1.2.2
- laravel/pint: ^1.24
- laravel/sail: ^1.41
- mockery/mockery: ^1.6
- nunomaduro/collision: ^8.6
- pestphp/pest: ^4.1
- pestphp/pest-plugin-laravel: ^4.0
This package is auto-updated.
Last update: 2026-06-17 14:00:05 UTC
README
Client package para o servidor NotifyKit. Integra 10 canais de notificação, campanhas em massa e consultas de status via API do servidor. É um cliente puro — não persiste nada localmente; todo o estado vive no servidor.
Índice
- Instalação
- Configuração
- Canais disponíveis
- Notificações individuais
- Campanhas em massa
- Estado e rastreamento
- Webhook receiver
- Consultas de status
- Eventos Laravel
Instalação
composer require risetechapps/notify-service-for-laravel
Publique a config:
php artisan vendor:publish --provider="RiseTechApps\Notify\NotifyServiceProvider" --tag="config" composer dump-autoload
O pacote não persiste nada localmente — não há migrations nem tabelas. Todo o histórico/estado fica no servidor; consulte via
NotifyQuery::server()e reaja aos callbacks pelos eventos de webhook (veja Webhook receiver).
Configuração
Adicione as variáveis ao seu .env:
NOTIFY_SERVICE_KEY=sua-api-key NOTIFY_SERVICE_WEBHOOK=https://sua-app.com/notify/webhook # Rotas automáticas de webhook (opcional) NOTIFY_SERVICE_ROUTES=true NOTIFY_SERVICE_ROUTES_PREFIX=notify
Arquivo config/notify.php:
return [ 'key' => env('NOTIFY_SERVICE_KEY', ''), 'webhook' => env('NOTIFY_SERVICE_WEBHOOK', ''), // Registra automaticamente as rotas de webhook do package 'routes' => env('NOTIFY_SERVICE_ROUTES', true), 'routes_prefix' => env('NOTIFY_SERVICE_ROUTES_PREFIX', 'notify'), 'routes_middleware' => ['api'], ];
Quando routes = true, o package registra automaticamente:
POST /notify/webhook → recebe status de notificações individuais
POST /notify/webhook/campaign → recebe status de campanhas
Para registrar manualmente (defina routes = false):
// routes/api.php use RiseTechApps\Notify\Http\Controllers\NotifyWebhookController; Route::post('/notify/webhook', [NotifyWebhookController::class, 'notification']); Route::post('/notify/webhook/campaign', [NotifyWebhookController::class, 'campaign']);
Canais disponíveis
| Canal | Drivers suportados | Método via() |
|---|---|---|
| SMS | Mobizon, ClickSend, Twilio | notify.sms |
| SMTP, Mailgun, Resend, SendGrid, SES, Postmark | notify.mail |
|
| Push Android | FCM | notify.push |
| Push iOS | APNS | notify.apns |
| Telegram | Telegram Bot API | notify.telegram |
| Slack | Slack Webhooks | notify.slack |
| Discord | Discord Webhooks | notify.discord |
| Teams | Microsoft Teams Webhooks | notify.teams |
| WebSocket | Pusher | notify.websocket |
| Webhook | HTTP genérico | notify.webhook |
Notificações individuais
Todas as notificações usam o sistema de notificações nativo do Laravel. Crie uma classe de notificação, declare o canal em via() e implemente o método correspondente.
O envio faz um POST ao servidor; nada é persistido localmente. Acompanhe o status
pelos eventos e consulte o histórico via NotifyQuery::server().
SMS
use RiseTechApps\Notify\Message\NotifySms; class PedidoConfirmado extends Notification { public function via($notifiable): array { return ['notify.sms']; } public function toNotifySms($notifiable): NotifySms { return (new NotifySms) ->to($notifiable->phone) ->content('Seu pedido #1234 foi confirmado!') ->tag(['pedido', 'transacional']) // opcional, agrupa/filtra ->configId('Twilio Principal') // opcional, UUID ou label ->webhookUrl('https://sua-app.com/notify/webhook'); // opcional } }
| Método | Descrição |
|---|---|
->to(string) |
Número destino, ex: +5521981425950 (máx: 32 chars) |
->content(string) |
Texto do SMS. Máx: 160 chars (sanitizado GSM-7 no servidor) |
->tag(string|array) |
Tag(s) para agrupar/filtrar. Acumula entre chamadas e é mesclada com as tags default do config |
->configId(string) |
Provedor/credencial específico (UUID ou label). Sobrescreve notify.sms.config_id |
->webhookUrl(string) |
URL de callback de status (sobrescreve o global do config) |
O remetente não é mais definido aqui — o servidor usa a credencial do provedor (config default ou a indicada por
config_id).
Defaults no config/notify.php:
'sms' => [ 'config_id' => env('NOTIFY_SMS_CONFIG_ID'), // usado quando a notificação não passa ->configId() 'tags' => ['minha-app'], // aplicadas a todo SMS, mescladas com as da notificação ],
Precedência: ->configId() da notificação sobrescreve o config_id do config; as tags
da notificação são somadas às tags default do config (sem duplicar).
Envio on-demand (sem model, direto para um número):
use Illuminate\Support\Facades\Notification; Notification::route('sms', '+5511999998888')->notify(new PedidoConfirmado());
Ciclo de vida completo do SMS:
| Etapa | Como |
|---|---|
| Enviar | $user->notify(...) ou Notification::route('sms', $numero)->notify(...) |
| Acompanhar status | eventos NotifySentEvent (envio) e NotifyWebhookEvent (callbacks) — ver Eventos |
| Listar histórico | NotifyQuery::server()->sms()->tag('promo')->status('delivered')->get() |
| Detalhe + timeline | NotifyQuery::server()->sms('uuid')->get() |
Cancelar (se created) |
NotifyQuery::server()->sms('uuid')->cancel() |
Detalhes desses métodos em Consultas de status → SMS.
Agendamento: o envio individual de SMS não suporta agendamento — para SMS agendado, use uma campanha SMS com
->scheduledAt(...).
use RiseTechApps\Notify\Message\NotifyMail; class BemVindo extends Notification { public function via($notifiable): array { return ['notify.mail']; } public function toNotifyMail($notifiable): NotifyMail { return (new NotifyMail) ->to($notifiable->email, $notifiable->name) // nome é obrigatório no servidor ->from('noreply@app.com', 'Minha App') // ou defina os defaults no config ->subject('Bem-vindo!') ->line('Obrigado por se cadastrar.') ->action('https://app.com/dashboard', 'Acessar painel') ->setSignature('Equipe Minha App') ->tag(['onboarding', 'transacional']) // opcional, agrupa/filtra ->configId('Resend Transacional'); // opcional, UUID ou label } }
Destinatário e remetente (campos obrigatórios)
O servidor exige email, name, email_from, name_from e app_name. Resolva assim:
| Campo | Como preencher |
|---|---|
email / name (destinatário) |
->to($email, $name) ou faça routeNotificationForMail() devolver [$email => $name] |
email_from / name_from (remetente) |
->from($email, $name) ou os defaults notify.mail.from.* no config/.env |
app_name |
->appName(...) ou notify.mail.app_name; cai para config('app.name') |
⚠️ Se nenhum desses for definido, o servidor responde
422comemail_from / name / name_from is required. Onamedo destinatário não é deduzido automaticamente — defina-o via->to()ou norouteNotificationForMail().
No seu Notifiable, para enviar o nome do destinatário junto:
public function routeNotificationForMail(): array { return [$this->email => $this->name]; }
Métodos
Cabeçalho / remetente:
| Método | Descrição |
|---|---|
->to(email, name?) |
Destinatário (write-once) |
->from(email, name?) |
Remetente (write-once; default em notify.mail.from.*) |
->appName(string) |
Nome do app/marca no template (default notify.mail.app_name → config('app.name')) |
->subject(string) |
Assunto (máx 255) |
->subjectMessage(string) |
Assunto detalhado; tem prioridade sobre subject no servidor (máx 1000) |
->theme(string) |
Tema do template. Default: notify.mail.theme (default) |
Corpo:
| Método | Descrição |
|---|---|
->line(string) |
Parágrafo principal (máx 1000) |
->lineHeader(string) |
Linha antes do corpo (acumula entre chamadas) |
->lineFooter(string) |
Linha após o corpo (acumula entre chamadas) |
->action(url, text) |
Botão CTA (a url é obrigatória quando há action) |
->addTable(EmailTable|array) |
Adiciona uma tabela: ['headers' => [], 'rows' => [[]]] |
->tables(array) |
Substitui todas as tabelas de uma vez (reseta as anteriores) |
->table(callable) |
Adiciona uma tabela via callback EmailTable fluente |
->addList(type, items) |
Adiciona uma lista: tipo ordered ou unordered |
->lists(array) |
Substitui todas as listas de uma vez |
->setSignature(string) |
Assinatura — acumula linha a linha |
->signature(string|array) |
Define a assinatura inteira de uma vez |
->attachFromUrl(string|array) |
Anexa arquivo(s) por URL (acumula) |
Roteamento / rastreio:
| Método | Descrição |
|---|---|
->tag(string|array) |
Tag(s) para agrupar/filtrar. Acumula e mescla com notify.mail.tags |
->configId(string) |
Credencial específica (UUID ou label). Sobrescreve notify.mail.config_id |
->webhookUrl(string) |
URL de callback de status (sobrescreve o global do config) |
⚠️
addTable()seguido detables()descarta a tabela doaddTable— otables()reseta a lista. Para múltiplas tabelas useaddTable()repetido ou sótables([...]).
Defaults no config/notify.php
'mail' => [ 'from' => [ 'address' => env('NOTIFY_MAIL_FROM_ADDRESS'), // remetente default 'name' => env('NOTIFY_MAIL_FROM_NAME'), ], 'app_name' => env('NOTIFY_MAIL_APP_NAME'), // cai para config('app.name') 'theme' => env('NOTIFY_MAIL_THEME', 'default'), 'config_id' => env('NOTIFY_MAIL_CONFIG_ID'), // usado quando não passa ->configId() 'tags' => [], // mescladas com as da notificação ],
NOTIFY_MAIL_FROM_ADDRESS=no-reply@suaempresa.com NOTIFY_MAIL_FROM_NAME="Sua Empresa" NOTIFY_MAIL_APP_NAME=NotifyApp NOTIFY_MAIL_CONFIG_ID=smtp-principal
Com os defaults configurados, o mínimo vira só ->to() + ->subject() + ->line().
Se você publicou o
config/notify.phpantes da versão com o bloco--force(ou adicione o bloco manualmente), senão os defaults de e-mail não existem.
Precedência: ->configId()/->appName()/->from() da notificação sobrescrevem os defaults;
as tags da notificação são somadas às notify.mail.tags (sem duplicar).
Envio on-demand (sem model)
use Illuminate\Support\Facades\Notification; Notification::route('mail', ['cliente@exemplo.com' => 'Maria Silva']) ->notify(new BemVindo());
Ciclo de vida completo do e-mail
| Etapa | Como |
|---|---|
| Enviar | $user->notify(...) ou Notification::route('mail', [...])->notify(...) |
| Acompanhar status | eventos NotifySentEvent e NotifyWebhookEvent — ver Eventos |
| Listar histórico | NotifyQuery::server()->mail()->tag('pedido')->status('delivered')->get() |
| Detalhe + timeline | NotifyQuery::server()->mail('uuid')->get() |
Cancelar (se created) |
NotifyQuery::server()->mail('uuid')->cancel() |
Detalhes desses métodos em Consultas de status → Email.
Push FCM
use RiseTechApps\Notify\Message\NotifyPush; public function toNotifyPush($notifiable): NotifyPush { return (new NotifyPush) ->token($notifiable->fcm_token) // string ou array de tokens ->title('Pedido a caminho 🚚') ->body('Seu pedido #123 saiu para entrega.') ->image('https://app.com/banner.png') ->channelId('pedidos') ->priority('high') ->ttl(600) ->link('https://app.com/pedidos/123') ->data(['route' => '/pedidos/123']); // valores devem ser strings (regra FCM) }
Alvo (use um dos três):
| Método | Descrição |
|---|---|
->token(string|array) |
Device token(s) FCM. Write-once: o primeiro valor não-vazio prevalece |
->topic(string) |
Tópico FCM — todos os inscritos |
->condition(string) |
Combina tópicos, ex: "'a' in topics && 'b' in topics" |
Conteúdo:
| Método | Descrição |
|---|---|
->title(string) |
Título da notificação |
->body(string) |
Corpo da notificação |
->image(string) |
URL da imagem grande (->imageUrl() é alias) |
->icon(string) |
URL do ícone pequeno |
->data(array) |
Dados extras (todos os valores como string) |
title/bodysão obrigatórios — exceto quandosilent(true)(push data-only).
Comportamento:
| Método | Descrição |
|---|---|
->sound(string|array) |
Nome do arquivo de som; ou objeto de som crítico iOS ['name' => 'alarm.caf', 'volume' => 1.0] (atalho: ->criticalSound()) |
->criticalSound(name, volume?) |
Som crítico no iOS (toca no modo silencioso). Gera ['name' => ..., 'volume' => ...]; combine com ->interruptionLevel('critical') |
->channelId(string) |
Android notification channel id (necessário p/ som/vibração no Android 8+) |
->badge(int) |
Número no badge (0 limpa) |
->priority(string) |
Entrega: high | normal |
->ttl(int|string) |
Time-to-live em segundos (600) ou string ("3600s") |
->collapseKey(string) |
Agrupa/substitui notificações pela mesma chave |
->silent(bool?) |
Push data-only / background sync (sem alerta visível) |
iOS / ações:
| Método | Descrição |
|---|---|
->clickAction(string) |
Android intent action ao tocar |
->category(string) |
Categoria de action buttons no iOS |
->interruptionLevel(string) |
passive | active | time-sensitive | critical |
Heads-up / agrupamento / interação:
| Método | Descrição |
|---|---|
->tag(string) |
Agrupa/substitui no device (Android+web) e etiqueta o histórico. String única (no push não é array); sobrescreve notify.push.tag |
->notificationPriority(string) |
Heads-up Android: PRIORITY_HIGH | PRIORITY_MAX ... |
->requireInteraction(bool?) |
Fixa a notificação até o usuário interagir (web) |
->action(title, id, icon?) |
Adiciona um botão (máx 3). Acumula entre chamadas |
->actions(array) |
Define todos os botões de uma vez |
->line(string) |
Adiciona um item de lista InboxStyle (máx 10, via data). Acumula |
->lines(array) |
Define todos os itens de lista de uma vez |
Extras:
| Método | Descrição |
|---|---|
->link(string) |
Deep link / URL aberta ao tocar (web push) |
->analyticsLabel(string) |
Rótulo de analytics no Firebase |
->android(array) |
Override bruto do bloco android do FCM |
->apns(array) |
Override bruto do bloco apns do FCM |
->webpush(array) |
Override bruto do bloco webpush do FCM |
->configId(string) |
Credencial FCM (UUID ou label). Sobrescreve notify.push.config_id |
->webhookUrl(string) |
URL de callback de status (default: notify.webhook global) |
⚠️ No push,
tagé string única (é a FCM grouping tag, regra de validação do servidor) — diferente de SMS/e-mail, aqui não aceita array.
Defaults no config/notify.php:
'push' => [ 'config_id' => env('NOTIFY_PUSH_CONFIG_ID'), // usado quando não passa ->configId() 'tag' => env('NOTIFY_PUSH_TAG'), // string única; sobrescrita por ->tag() ],
Exemplos avançados:
// Som crítico no iOS (exige permissão de "critical alerts" no app) (new NotifyPush) ->token($fcm) ->title('ALERTA') ->body('Temperatura acima do limite!') ->criticalSound('alarm.caf', 1.0) ->interruptionLevel('critical') ->priority('high'); // Tópico + imagem + heads-up + badge + tag de agrupamento (new NotifyPush) ->topic('promocoes') ->title('Black Friday começou!') ->body('Até 70% OFF só hoje.') ->image('https://cdn.empresa.com/promo.jpg') ->channelId('ofertas') ->notificationPriority('PRIORITY_MAX') ->badge(1) ->tag('blackfriday'); // Botões + lista (InboxStyle) + deep link (new NotifyPush) ->token($fcm) ->title('3 novas mensagens') ->body('Toque para ver') ->line('Ana: bom dia!') ->line('João: reunião 14h') ->line('Maria: enviei o arquivo') ->action('Responder', 'reply') ->action('Marcar como lida', 'mark_read') ->clickAction('OPEN_CHAT') ->data(['screen' => 'chat', 'chat_id' => '987']); // Push silencioso / data-only (new NotifyPush)->token($fcm)->silent()->data(['sync' => 'contacts', 'version' => '42']); // Override bruto por plataforma (new NotifyPush) ->token($fcm)->title('Custom')->body('Avançado') ->android(['notification' => ['color' => '#E53935', 'notification_priority' => 'PRIORITY_MAX']]) ->apns(['payload' => ['aps' => ['thread-id' => 'grupo-pedidos']]]);
Ciclo de vida completo do push:
| Etapa | Como |
|---|---|
| Enviar | $user->notify(...) (token via routeNotificationFor('push') ou ->token()) |
| Acompanhar status | eventos NotifySentEvent e NotifyWebhookEvent — ver Eventos |
| Listar histórico | NotifyQuery::server()->push()->tag('promo')->status('sent')->get() |
| Detalhe + timeline | NotifyQuery::server()->push('uuid')->get() |
Cancelar (se created) |
NotifyQuery::server()->push('uuid')->cancel() |
Detalhes em Consultas de status → Push.
Tokens inválidos: quando você envia para um array de tokens, o servidor reporta os mortos no webhook pelo evento
token_invalid(campotokens: [...]no payload doNotifyWebhookEvent) — use para limpar seu store.
APNS Apple Push
Push direto para iOS via APNs. Aceita token único ou lista de tokens; o servidor enfileira
(resposta 202 com notification_id) e devolve o status pelo webhook.
use RiseTechApps\Notify\Message\NotifyApns; public function toNotifyApns($notifiable): NotifyApns { return (new NotifyApns) ->token($notifiable->apns_token) // string ou array de tokens ->title('Novo pedido') ->subtitle('Pedido #1234') ->body('Seu pedido foi enviado.') ->badge(1) ->sound('default') ->category('ORDER') ->threadId('pedidos') ->tag(['entregas', 'transacional']) // opcional, agrupa/filtra ->configId('APNS Produção') // opcional, UUID ou label ->data(['order_id' => '1234']); }
| Método | Descrição |
|---|---|
->token(string|array) |
Device token(s) Apple. Write-once: o primeiro valor não-vazio prevalece |
->title(string) |
Título |
->body(string) |
Corpo |
->subtitle(string) |
Subtítulo |
->badge(int) |
Número no badge do ícone (0 limpa) |
->sound(string) |
Nome do arquivo de som (ex.: default) |
->data(array) |
Payload customizado entregue ao app |
->category(string) |
Categoria para action buttons iOS |
->threadId(string) |
Agrupa notificações relacionadas no device |
->tag(string|array) |
Tag(s) para agrupar/filtrar. Acumula e mescla com notify.apns.tags |
->configId(string) |
Credencial APNS (UUID ou label). Sobrescreve notify.apns.config_id |
->webhookUrl(string) |
URL de callback de status (default: notify.webhook global) |
Defaults no config/notify.php:
'apns' => [ 'config_id' => env('NOTIFY_APNS_CONFIG_ID'), // usado quando não passa ->configId() 'tags' => [], // mescladas com as da notificação ],
Precedência: ->configId() da notificação sobrescreve o config_id do config; as tags
da notificação são somadas às notify.apns.tags (sem duplicar).
Envio on-demand (sem model, direto para um token):
use Illuminate\Support\Facades\Notification; Notification::route('apns', $apnsToken)->notify(new PedidoEnviado());
Ciclo de vida completo do APNs:
| Etapa | Como |
|---|---|
| Enviar | $user->notify(...) (token via routeNotificationFor('apns') ou ->token()) |
| Acompanhar status | eventos NotifySentEvent e NotifyWebhookEvent — ver Eventos |
| Listar histórico | NotifyQuery::server()->apns()->tag('entregas')->status('sent')->get() |
| Detalhe + timeline | NotifyQuery::server()->apns('uuid')->get() |
Cancelar (se created) |
NotifyQuery::server()->apns('uuid')->cancel() |
Detalhes em Consultas de status → APNS.
Telegram
Envia texto, foto, localização, contato, enquete e botões inline. O servidor enfileira
(resposta 202 com notification_id) e devolve o status pelo webhook. Cada envio carrega
um tipo de conteúdo.
use RiseTechApps\Notify\Message\NotifyTelegram; class DeployConcluido extends Notification { public function via($notifiable): array { return ['notify.telegram']; } public function toNotifyTelegram($notifiable): NotifyTelegram { return (new NotifyTelegram) ->chatId($notifiable->telegram_chat_id) ->message('🚀 Seu deploy foi concluído com sucesso!') ->parseMode('Markdown') ->disablePreview() ->button('Ver logs', 'https://app.com/logs'); } }
O chat_id também pode vir do routeNotificationForTelegram() no seu Notifiable:
public function routeNotificationForTelegram(): string { return $this->telegram_chat_id; }
Conteúdo (use um por envio):
| Método | Descrição |
|---|---|
->message(string) |
Texto da mensagem. Máx: 4096 chars |
->imageUrl(string) |
Envia uma foto a partir de uma URL (o message vira legenda) |
->location(lat, lng) |
Envia uma localização (pino no mapa) |
->contact(phone, firstName, lastName?) |
Envia um cartão de contato |
->poll(question, options[], isAnonymous?) |
Envia uma enquete (isAnonymous default true) |
Alvo / formatação:
| Método | Descrição |
|---|---|
->chatId(string) |
ID numérico, @username ou alias de canal (ex.: equipe) (obrigatório). Write-once |
->parseMode(string) |
Markdown | MarkdownV2 | HTML. Default: Markdown |
->button(text, url) |
Botão inline (cada chamada = uma linha) |
->buttons(array) |
Grade completa: [[['text' => '', 'url' => '']]] (substitui) |
Opções comuns (combinam com qualquer conteúdo):
| Método | Descrição |
|---|---|
->silent(bool?) |
Envio silencioso (disable_notification) |
->protectContent(bool?) |
Impede encaminhamento/cópia |
->replyTo(int) |
Responde a uma mensagem (message_id do Telegram) |
->threadId(int) |
Envia para um tópico de grupo/fórum |
->disablePreview(bool?) |
Desabilita preview de links |
->tag(string|array) |
Tag(s) para agrupar/filtrar. Acumula e mescla com notify.telegram.tags |
->configId(string) |
Bot/credencial (UUID ou label). Sobrescreve notify.telegram.config_id |
->webhookUrl(string) |
URL de callback de status (default: notify.webhook global) |
Defaults no config/notify.php:
'telegram' => [ 'config_id' => env('NOTIFY_TELEGRAM_CONFIG_ID'), // usado quando não passa ->configId() 'tags' => [], // mescladas com as da notificação ],
Precedência: ->configId() da notificação sobrescreve o config_id do config; as tags
da notificação são somadas às notify.telegram.tags (sem duplicar).
Exemplos por tipo de conteúdo
use RiseTechApps\Notify\Message\NotifyTelegram; // Texto simples (new NotifyTelegram)->chatId($chatId)->message('Olá! 👋'); // Texto com formatação HTML (new NotifyTelegram) ->chatId($chatId) ->message('<b>Pedido #123</b> confirmado!') ->parseMode('HTML'); // Foto a partir de URL (message vira a legenda) (new NotifyTelegram) ->chatId($chatId) ->imageUrl('https://cdn.app.com/banner.jpg') ->message('Confira a novidade!'); // Localização (pino no mapa) (new NotifyTelegram)->chatId($chatId)->location(-23.5614, -46.6559); // Contato (lastName é opcional) (new NotifyTelegram)->chatId($chatId)->contact('+5511999998888', 'Suporte', 'NotifyApp'); // Enquete anônima (padrão) (new NotifyTelegram)->chatId($chatId)->poll('Gostou?', ['Sim', 'Não']); // Enquete não-anônima (new NotifyTelegram)->chatId($chatId)->poll('Qual prefere?', ['A', 'B', 'C'], isAnonymous: false);
Exemplos de botões inline
// Um botão por linha (cada ->button() vira uma nova linha) (new NotifyTelegram) ->chatId($chatId) ->message('Escolha uma ação:') ->button('Ver pedido', 'https://app.com/pedidos/1') ->button('Falar com suporte', 'https://app.com/suporte'); // Grade completa: array externo = linhas, interno = botões na mesma linha (new NotifyTelegram) ->chatId($chatId) ->message('Confirma?') ->buttons([ [ ['text' => '✅ Sim', 'url' => 'https://app.com/sim'], ['text' => '❌ Não', 'url' => 'https://app.com/nao'], ], [ ['text' => 'Ver detalhes', 'url' => 'https://app.com/detalhes'], ], ]);
Exemplos de opções comuns
// Silencioso + protegido contra encaminhamento/cópia (new NotifyTelegram) ->chatId($chatId) ->message('Aviso confidencial') ->silent() ->protectContent(); // Responder a uma mensagem + enviar para um tópico de grupo/fórum (new NotifyTelegram) ->chatId($grupoId) ->message('Respondendo...') ->replyTo(4521) // message_id do Telegram da mensagem original ->threadId(12); // tópico do fórum // Tag + bot/credencial específico + webhook próprio (new NotifyTelegram) ->chatId($chatId) ->message('Pedido enviado') ->tag(['pedidos', 'transacional']) ->configId('Bot Vendas') ->webhookUrl('https://sua-app.com/notify/webhook');
Envio on-demand (sem model)
use Illuminate\Support\Facades\Notification; Notification::route('telegram', '123456789')->notify(new DeployConcluido());
Gerenciar mensagens enviadas
Edite, apague ou fixe uma mensagem já enviada pelo próprio recurso de consulta,
em NotifyQuery::server()->telegram($notificationId). Essas operações são síncronas
e usam apenas o notification_id — o UUID retornado no envio (202). O servidor
resolve o chat_id + message_id salvos, então você não precisa mais capturar o
message_id do Telegram no webhook.
O notification_id vem direto na resposta do envio, no NotifySentEvent:
use RiseTechApps\Notify\Events\NotifySentEvent; public function handle(NotifySentEvent $event): void { if ($event->channel === 'telegram') { $notificationId = $event->response['notification_id'] ?? null; // guarde onde quiser, se for precisar editar/fixar/apagar depois } }
use RiseTechApps\Notify\NotifyQuery; // Editar o texto (com parse mode opcional) NotifyQuery::server()->telegram($notificationId)->edit('Texto *editado* ✏️'); NotifyQuery::server()->telegram($notificationId)->edit('<b>Novo</b>', 'HTML'); // Editar a legenda de uma mídia NotifyQuery::server()->telegram($notificationId)->editCaption('Nova legenda'); // Apagar NotifyQuery::server()->telegram($notificationId)->delete(); // Fixar (opcionalmente sem notificar os membros) NotifyQuery::server()->telegram($notificationId)->pin(disableNotification: true); // Todas retornam: ['status' => bool, 'message' => string|null, 'http' => int]
Ciclo de vida completo do Telegram:
| Etapa | Como |
|---|---|
| Enviar | $user->notify(...) ou Notification::route('telegram', $chatId)->notify(...) |
| Acompanhar status | eventos NotifySentEvent e NotifyWebhookEvent — ver Eventos |
| Listar histórico | NotifyQuery::server()->telegram()->tag('pedidos')->status('sent')->get() |
| Detalhe + timeline | NotifyQuery::server()->telegram('uuid')->get() |
Cancelar (se created) |
NotifyQuery::server()->telegram('uuid')->cancel() |
| Editar/apagar/fixar | NotifyQuery::server()->telegram($notificationId)->edit(...) etc. |
Detalhes das consultas em Consultas de status → Telegram.
Slack
Funciona em dois modos, conforme a credencial no servidor: Bot Token (entrega no
channel informado) ou Incoming Webhook (entrega numa URL de webhook do Slack).
O servidor enfileira (resposta 202 com notification_id) e devolve o status pelo webhook.
use RiseTechApps\Notify\Message\NotifySlack; class FalhaPagamento extends Notification { public function via($notifiable): array { return ['notify.slack']; } public function toNotifySlack($notifiable): NotifySlack { return (new NotifySlack) ->channel('#alertas') ->title('Erro crítico') ->message('Ocorreu um erro na integração de pagamento.') ->color('#FF0000') ->field('Ambiente', 'Produção') ->field('Servidor', 'api-01') ->tag('alertas'); // opcional, agrupa/filtra } }
Conteúdo:
| Método | Descrição |
|---|---|
->message(string) |
Texto principal. Máx: 4000 chars (obrigatório, salvo com blocks ou arquivo) |
->channel(string) |
Canal destino (Bot Token), ex: #alerts, C01234ABC ou alias (ex.: equipe) |
->title(string) |
Título do attachment (ativa o card rico) |
->color(string) |
Cor hex da barra lateral: #FF0000 |
->footer(string) |
Rodapé do attachment |
->field(label, value, short?) |
Campo chave-valor no attachment (short = lado a lado) |
->fields(array) |
Define todos os campos de uma vez (substitui) |
->blocks(array) |
Block Kit cru (seções, botões, dividers, imagens). message vira fallback |
Threads / menções:
| Método | Descrição |
|---|---|
->thread(string) |
Responde em thread (o ts da mensagem-pai) |
->mentions(string|array) |
Menções (acumula): U123 (user), C123 (canal), channel, here |
Aparência:
| Método | Descrição |
|---|---|
->username(string) |
Sobrescreve o nome do remetente |
->iconEmoji(string) |
Ícone por emoji, ex: :rocket: |
->iconUrl(string) |
Ícone por URL |
Arquivo (Bot Token):
| Método | Descrição |
|---|---|
->file(url, title?, filename?) |
Anexa um arquivo por URL |
Transporte / servidor:
| Método | Descrição |
|---|---|
->slackWebhookUrl(string) |
Força o modo Incoming Webhook (canal fixo; o channel é ignorado) |
->tag(string|array) |
Tag(s) para agrupar/filtrar. Acumula e mescla com notify.slack.tags |
->configId(string) |
Credencial Slack (UUID ou label). Sobrescreve notify.slack.config_id |
->webhookUrl(string) |
URL de callback de status (default: notify.webhook global) |
⚠️ Não confunda
->slackWebhookUrl()(destino de entrega no Slack, modo Incoming Webhook) com->webhookUrl()(callback de status que o seu app recebe).
Modos de corpo (prioridade no servidor): 1) blocks (Block Kit) → message vira
fallback · 2) attachment (quando há title/fields) → card colorido · 3) texto simples
(só message).
Modos de transporte: Bot Token (chat.postMessage) — qualquer canal, habilita
thread, upload e editar/apagar · Incoming Webhook (->slackWebhookUrl()) — canal fixo,
mais simples.
Defaults no config/notify.php:
'slack' => [ 'config_id' => env('NOTIFY_SLACK_CONFIG_ID'), // usado quando não passa ->configId() 'tags' => [], // mescladas com as da notificação ],
Precedência: ->configId() da notificação sobrescreve o config_id do config; as tags
da notificação são somadas às notify.slack.tags (sem duplicar).
Formas de disparo
Toda mensagem Slack é construída com NotifySlack e enviada pelo sistema de notificações
do Laravel. Há três formas:
use Illuminate\Support\Facades\Notification; // 1. A partir de um model Notifiable (canal vem do routeNotificationForSlack(), se houver) $user->notify(new FalhaPagamento()); // 2. On-demand, sem model — o alvo só é usado se a mensagem não trouxer ->channel()/->slackWebhookUrl() Notification::route('slack', '#deploys')->notify(new FalhaPagamento());
Para testar rapidamente qualquer corpo de mensagem, crie uma notification genérica que
recebe um NotifySlack pronto:
namespace App\Notifications; use Illuminate\Notifications\Notification; use RiseTechApps\Notify\Message\NotifySlack; class TestSlack extends Notification { public function __construct(protected NotifySlack $slack) {} public function via($notifiable): array { return ['notify.slack']; } public function toNotifySlack($notifiable): NotifySlack { return $this->slack; } }
use Illuminate\Support\Facades\Notification; use App\Notifications\TestSlack; use RiseTechApps\Notify\Message\NotifySlack; // 3. Dispare qualquer um dos exemplos abaixo: Notification::route('slack', '#deploys')->notify(new TestSlack( (new NotifySlack)->channel('#deploys')->message('Teste 🚀') ));
Exemplos por possibilidade
Cada bloco abaixo é o NotifySlack que você retorna no toNotifySlack() (ou passa para a
TestSlack acima):
use RiseTechApps\Notify\Message\NotifySlack; // Texto simples (new NotifySlack)->channel('#deploys')->message('Deploy concluído 🚀')->tag('deploys'); // Attachment com campos (short = lado a lado) (new NotifySlack) ->channel('#alertas') ->title('CPU 90%') ->message('Alerta') ->color('#FF0000') ->field('Servidor', 'web-01', short: true) ->footer('Monitoramento'); // Block Kit (botões) (new NotifySlack) ->channel('#deploys') ->message('Deploy v1.2.0') // fallback ->blocks([ ['type' => 'section', 'text' => ['type' => 'mrkdwn', 'text' => '*Deploy v1.2.0* :rocket:']], ['type' => 'divider'], ['type' => 'actions', 'elements' => [ ['type' => 'button', 'text' => ['type' => 'plain_text', 'text' => 'Aprovar'], 'style' => 'primary', 'url' => 'https://ci/482'], ]], ]); // Menções (new NotifySlack)->channel('#incidentes')->message('Incidente aberto.')->mentions(['here', 'U024BE7LH']); // Thread (responde à mensagem-pai pelo ts) (new NotifySlack)->channel('#deploys')->message('Deploy concluído ✅')->thread('1718150400.123456'); // Incoming Webhook + aparência (channel é ignorado) (new NotifySlack) ->message('Backup ok.') ->slackWebhookUrl('https://hooks.slack.com/services/T/B/x') ->username('Backup Bot') ->iconEmoji(':floppy_disk:'); // Upload de arquivo (Bot Token) (new NotifySlack) ->channel('#relatorios') ->message('Relatório em anexo:') ->file('https://storage.empresa.com/maio.pdf', 'Maio/2026'); // Aparência com ícone por URL (new NotifySlack) ->channel('#geral') ->message('Olá do bot 👋') ->username('Status Bot') ->iconUrl('https://app.com/bot-avatar.png'); // Credencial específica (multi-tenant) + múltiplas tags (new NotifySlack) ->channel('#vendas') ->message('Nova venda registrada') ->configId('Slack Workspace Vendas') // UUID ou label ->tag(['vendas', 'transacional']); // Callback de status próprio deste envio (≠ slackWebhookUrl) (new NotifySlack) ->channel('#deploys') ->message('Pipeline iniciado') ->webhookUrl('https://sua-app.com/notify/webhook');
O canal destino também pode vir do routeNotificationForSlack() no seu Notifiable — ele
é usado apenas quando a mensagem não traz ->channel() nem ->slackWebhookUrl():
public function routeNotificationForSlack(): string { return '#minha-equipe'; }
Envio on-demand (sem model):
use Illuminate\Support\Facades\Notification; Notification::route('slack', '#deploys')->notify(new FalhaPagamento());
Gerenciar mensagens enviadas (só Bot Token)
Edite ou apague uma mensagem já enviada pelo recurso de consulta, em
NotifyQuery::server()->slack($notificationId). São operações síncronas e funcionam
apenas no modo Bot Token (Incoming Webhook não permite editar/apagar). O
notification_id vem na resposta do envio (NotifySentEvent->response['notification_id']).
use RiseTechApps\Notify\NotifyQuery; // Editar (chat.update) NotifyQuery::server()->slack($notificationId)->edit('Deploy *concluído* ✅'); // Apagar (chat.delete) NotifyQuery::server()->slack($notificationId)->delete(); // Ambas retornam: ['status' => bool, 'message' => string|null, 'http' => int]
Ciclo de vida completo do Slack:
| Etapa | Como |
|---|---|
| Enviar | $user->notify(...) ou Notification::route('slack', $canal)->notify(...) |
| Acompanhar status | eventos NotifySentEvent e NotifyWebhookEvent — ver Eventos |
| Listar histórico | NotifyQuery::server()->slack()->tag('deploys')->status('sent')->get() |
| Detalhe + timeline | NotifyQuery::server()->slack('uuid')->get() |
Cancelar (se created) |
NotifyQuery::server()->slack('uuid')->cancel() |
| Editar/apagar (Bot Token) | NotifyQuery::server()->slack($notificationId)->edit(...) / ->delete() |
Detalhes em Consultas de status → Slack.
Discord
Envia texto simples ou embed rico (título, cor, campos, imagem, thumbnail, rodapé).
O servidor enfileira (resposta 202 com notification_id) e devolve o status pelo webhook.
use RiseTechApps\Notify\Message\NotifyDiscord; class NovoCadastro extends Notification { public function via($notifiable): array { return ['notify.discord']; } public function toNotifyDiscord($notifiable): NotifyDiscord { return (new NotifyDiscord) ->username('NotifyBot') ->title('Novo usuário cadastrado') ->message('João Silva acabou de se cadastrar.') ->color(0x2ecc71) // verde (hex) — ou 3066993 em decimal ->field('Email', 'joao@email.com') ->field('Plano', 'Pro', inline: true) ->tag('cadastros'); // opcional, agrupa/filtra } }
Destino (precedência: discordWebhookUrl › channel alias › webhook padrão da config):
| Método | Descrição |
|---|---|
->channel(string) |
Alias do canal (ex.: equipe); o servidor resolve para a webhook URL |
->discordWebhookUrl(string) |
Webhook URL direta (override; ver em Transporte) |
Conteúdo / embed:
| Método | Descrição |
|---|---|
->message(string) |
Texto da mensagem. Máx: 2000 chars (obrigatório, salvo com embeds/arquivo) |
->username(string) |
Sobrescreve o nome do webhook. Máx: 80 chars |
->avatarUrl(string) |
Sobrescreve o avatar do webhook (URL) |
->title(string) |
Título do embed (atalho). Máx: 256 chars |
->color(int) |
Cor do embed em decimal/hex, ex: 0xe74c3c (vermelho) |
->imageUrl(string) |
Imagem grande no embed |
->thumbnail(string) |
Miniatura no embed |
->footer(string) |
Rodapé do embed. Máx: 2048 chars |
->field(label, value, inline?) |
Campo do embed (inline default true; máx 25) |
->fields(array) |
Define todos os campos de uma vez (substitui) |
->embeds(array) |
Embeds crus do Discord (máx 10). Têm prioridade sobre o embed-atalho |
Menções:
| Método | Descrição |
|---|---|
->mentions(string|array) |
Menções (acumula): "123" (user), "&456" (cargo/role), "everyone", "here" |
->allowedMentions(array) |
Override cru do allowed_mentions nativo (sobrescreve o derivado de mentions) |
Thread / extras:
| Método | Descrição |
|---|---|
->threadId(string) |
Posta numa thread já existente (canal de texto) |
->threadName(string) |
Cria uma thread nova (só em canais de fórum/mídia). Máx: 100 chars |
->tts(bool?) |
Text-to-speech |
->file(url, filename?) |
Anexa um arquivo por URL (o servidor baixa e faz o upload) |
Transporte / servidor:
| Método | Descrição |
|---|---|
->discordWebhookUrl(string) |
Webhook de destino do Discord (override da config) |
->tag(string|array) |
Tag(s) para agrupar/filtrar. Acumula e mescla com notify.discord.tags |
->configId(string) |
Credencial Discord (UUID ou label). Sobrescreve notify.discord.config_id |
->webhookUrl(string) |
URL de callback de status (default: notify.webhook global) |
⚠️ Não confunda
->discordWebhookUrl()(destino de entrega no Discord) com->webhookUrl()(callback de status que o seu app recebe). O card rico do Discord sãoembeds(≠ do Slack, que usablocks); a cor éintdecimal/hex; menção de cargo usa o prefixo&.
Defaults no config/notify.php:
'discord' => [ 'config_id' => env('NOTIFY_DISCORD_CONFIG_ID'), // usado quando não passa ->configId() 'tags' => [], // mescladas com as da notificação ],
Precedência: ->configId() da notificação sobrescreve o config_id do config; as tags
da notificação são somadas às notify.discord.tags (sem duplicar).
Formas de disparo
Toda mensagem Discord é construída com NotifyDiscord e enviada pelo sistema de
notificações do Laravel. Há três formas:
use Illuminate\Support\Facades\Notification; // 1. A partir de um model Notifiable (destino vem do routeNotificationForDiscord(), se houver) $user->notify(new NovoCadastro()); // 2. On-demand, sem model Notification::route('discord', $webhookOuId)->notify(new NovoCadastro());
public function routeNotificationForDiscord(): string { return 'https://discord.com/api/webhooks/123/abc'; // ou um id/label de config }
Para testar rapidamente qualquer corpo de mensagem, crie uma notification genérica que
recebe um NotifyDiscord pronto:
namespace App\Notifications; use Illuminate\Notifications\Notification; use RiseTechApps\Notify\Message\NotifyDiscord; class TestDiscord extends Notification { public function __construct(protected NotifyDiscord $discord) {} public function via($notifiable): array { return ['notify.discord']; } public function toNotifyDiscord($notifiable): NotifyDiscord { return $this->discord; } }
use Illuminate\Support\Facades\Notification; use App\Notifications\TestDiscord; use RiseTechApps\Notify\Message\NotifyDiscord; // Dispare qualquer um dos exemplos abaixo: Notification::route('discord', 'default')->notify(new TestDiscord( (new NotifyDiscord)->message('Teste 🚀') ));
Exemplos por possibilidade
Cada bloco abaixo é o NotifyDiscord que você retorna no toNotifyDiscord() (ou passa
para a TestDiscord acima):
use RiseTechApps\Notify\Message\NotifyDiscord; // Texto simples (new NotifyDiscord)->message('Novo cadastro: João')->tag('cadastros'); // Por alias de canal (cadastrado na config — o servidor resolve a webhook URL) (new NotifyDiscord)->channel('equipe')->message('Deploy concluído ✅'); // Embed completo (atalho) (new NotifyDiscord) ->username('Status Bot') ->avatarUrl('https://app.com/bot.png') ->title('Build #482') ->message('Pipeline finalizado com sucesso.') ->color(0x2ecc71) ->field('Branch', 'main', inline: true) ->field('Duração', '3m12s', inline: true) ->thumbnail('https://cdn.app.com/ok.png') ->footer('CI/CD'); // Menções (role usa prefixo &) (new NotifyDiscord) ->message('Incidente aberto.') ->mentions(['here', '&987654321', '123456789']); // Controle fino das menções (allowed_mentions cru) (new NotifyDiscord) ->message('Aviso geral') ->allowedMentions(['parse' => ['everyone'], 'users' => ['123'], 'roles' => ['456']]); // Postar numa thread existente (new NotifyDiscord)->message('Atualização: mitigado.')->threadId('1112223334445556667'); // Criar thread nova (canal de fórum) (new NotifyDiscord)->message('Nova discussão')->threadName('Deploy 2026-06'); // Upload de arquivo por URL (o servidor baixa e envia) (new NotifyDiscord) ->message('Log em anexo:') ->file('https://storage.empresa.com/erro.log', 'erro.log'); // Embeds crus (prioridade sobre o atalho) (new NotifyDiscord) ->message('Resumo') ->embeds([ ['title' => 'Embed 1', 'description' => 'Linha', 'color' => 0x3498db], ['title' => 'Embed 2', 'description' => 'Outra'], ]); // Webhook de destino específico + TTS (new NotifyDiscord) ->message('Mensagem falada') ->tts() ->discordWebhookUrl('https://discord.com/api/webhooks/123/abc');
Gerenciar mensagens enviadas
Edite ou apague uma mensagem já enviada pelo recurso de consulta, em
NotifyQuery::server()->discord($notificationId). São operações síncronas que usam
apenas o notification_id (vem na resposta do envio, NotifySentEvent->response['notification_id']).
use RiseTechApps\Notify\NotifyQuery; // Editar (PATCH) NotifyQuery::server()->discord($notificationId)->edit('Incidente resolvido ✅'); // Apagar (DELETE) NotifyQuery::server()->discord($notificationId)->delete(); // Ambas retornam: ['status' => bool, 'message' => string|null, 'http' => int]
Ciclo de vida completo do Discord:
| Etapa | Como |
|---|---|
| Enviar | $user->notify(...) ou Notification::route('discord', $alvo)->notify(...) |
| Acompanhar status | eventos NotifySentEvent e NotifyWebhookEvent — ver Eventos |
| Listar histórico | NotifyQuery::server()->discord()->tag('cadastros')->status('sent')->get() |
| Detalhe + timeline | NotifyQuery::server()->discord('uuid')->get() |
Cancelar (se created) |
NotifyQuery::server()->discord('uuid')->cancel() |
| Editar/apagar | NotifyQuery::server()->discord($notificationId)->edit(...) / ->delete() |
Detalhes em Consultas de status → Discord.
Teams
Monta um card do Microsoft Teams (título, subtítulo, cor, imagem, facts e botões) — ou um
card cru via ->card(). O servidor enfileira (resposta 202 com notification_id) e
devolve o status pelo webhook.
use RiseTechApps\Notify\Message\NotifyTeams; class RelatorioSemanal extends Notification { public function via($notifiable): array { return ['notify.teams']; } public function toNotifyTeams($notifiable): NotifyTeams { return (new NotifyTeams) ->channel('equipe') // opcional: alias do canal ->title('Relatório semanal disponível') ->message('O relatório de vendas da semana está pronto.') ->color('0078D4') ->fact('Período', 'Mar/2026') ->fact('Total', 'R$ 48.200,00') ->action('Ver relatório', 'https://app.com/reports') ->tag('relatorios'); // opcional, agrupa/filtra } }
Destino (precedência: teamsWebhookUrl › channel alias › webhook padrão da config):
| Método | Descrição |
|---|---|
->channel(string) |
Alias do canal (ex.: equipe); o servidor resolve para a webhook URL |
->teamsWebhookUrl(string) |
Webhook URL direta de destino (override) |
Card:
| Método | Descrição |
|---|---|
->message(string) |
Corpo da mensagem. Máx: 4000 chars (obrigatório, salvo com card cru) |
->title(string) |
Título do card. Máx: 255 |
->subtitle(string) |
Subtítulo do card. Máx: 255 |
->color(string) |
Cor hex sem #, ex: 0078D4 (o # é removido automaticamente) |
->imageUrl(string) |
Imagem do card |
->fact(label, value) |
Par chave-valor no card |
->facts(array) |
Define todos os facts de uma vez (substitui) |
->action(label, url) |
Botão de ação |
->actions(array) |
Define todos os botões de uma vez (substitui) |
->card(array) |
MessageCard/Adaptive Card cru (passthrough; ignora o atalho acima) |
Servidor:
| Método | Descrição |
|---|---|
->tag(string|array) |
Tag(s) para agrupar/filtrar. Acumula e mescla com notify.teams.tags |
->configId(string) |
Credencial Teams (UUID ou label). Sobrescreve notify.teams.config_id |
->webhookUrl(string) |
URL de callback de status (default: notify.webhook global) |
⚠️ Não confunda
->teamsWebhookUrl()(destino de entrega no Teams) com->webhookUrl()(callback de status que o seu app recebe).
Defaults no config/notify.php:
'teams' => [ 'config_id' => env('NOTIFY_TEAMS_CONFIG_ID'), // usado quando não passa ->configId() 'tags' => [], // mescladas com as da notificação ],
Precedência: ->configId() da notificação sobrescreve o config_id do config; as tags
da notificação são somadas às notify.teams.tags (sem duplicar).
Exemplos:
use RiseTechApps\Notify\Message\NotifyTeams; // Texto simples (new NotifyTeams)->message('Build #482 finalizado ✅')->tag('deploys'); // Por alias de canal (new NotifyTeams)->channel('equipe')->message('Deploy de produção concluído.'); // Card completo (com subtítulo, imagem, facts e ações) (new NotifyTeams) ->title('Novo incidente') ->subtitle('payments-api') ->message('Falha no serviço de pagamento.') ->color('E81123') // vermelho ->imageUrl('https://cdn.app.com/alert.png') ->fact('Severidade', 'Alta') ->fact('Serviço', 'payments-api') ->action('Abrir runbook', 'https://app.com/runbooks/payments') ->action('Ver dashboard', 'https://app.com/status'); // Card cru (MessageCard/Adaptive Card) — passthrough total (new NotifyTeams)->card([ '@type' => 'MessageCard', '@context' => 'http://schema.org/extensions', 'summary' => 'Resumo', 'sections' => [['activityTitle' => 'Meu card', 'text' => 'Conteúdo']], ]); // Webhook de destino direto + callback de status próprio (new NotifyTeams) ->teamsWebhookUrl('https://outlook.office.com/webhook/X') ->message('Aviso') ->webhookUrl('https://sua-app.com/notify/webhook');
Envio on-demand (sem model):
use Illuminate\Support\Facades\Notification; Notification::route('teams', $idOuWebhook)->notify(new RelatorioSemanal());
Ciclo de vida completo do Teams:
| Etapa | Como |
|---|---|
| Enviar | $user->notify(...) ou Notification::route('teams', $alvo)->notify(...) |
| Acompanhar status | eventos NotifySentEvent e NotifyWebhookEvent — ver Eventos |
| Listar histórico | NotifyQuery::server()->teams()->tag('relatorios')->status('sent')->get() |
| Detalhe + timeline | NotifyQuery::server()->teams('uuid')->get() |
Cancelar (se created) |
NotifyQuery::server()->teams('uuid')->cancel() |
Detalhes em Consultas de status → Teams.
O Teams entrega via Incoming Webhook — não há editar/apagar mensagem como em Telegram/Slack/Discord.
WebSocket
Dispara um evento em tempo real em um canal público, privado ou de presença.
O servidor enfileira (resposta 202 com notification_id) e devolve o status pelo webhook.
use RiseTechApps\Notify\Message\NotifyWebSocket; class PedidoAtualizado extends Notification { public function via($notifiable): array { return ['notify.websocket']; } public function toNotifyWebSocket($notifiable): NotifyWebSocket { return (new NotifyWebSocket) ->channel("private-user.{$notifiable->id}") ->event('OrderStatusUpdated') ->data(['order_id' => 1234, 'status' => 'shipped']) ->private() ->tag('pedidos'); // opcional, agrupa/filtra } }
| Método | Descrição |
|---|---|
->channel(string) |
Canal, ex: notifications, private-user.123 |
->event(string) |
Nome do evento, ex: OrderUpdated |
->data(array) |
Payload do evento |
->private(bool?) |
Canal privado (prefixo private- aplicado pelo servidor; requer auth) |
->presence(bool?) |
Canal de presence (prefixo presence- aplicado pelo servidor) |
->tag(string|array) |
Tag(s) para agrupar/filtrar. Acumula e mescla com notify.websocket.tags |
->configId(string) |
Credencial (UUID ou label). Sobrescreve notify.websocket.config_id |
->webhookUrl(string) |
URL de callback de status (default: notify.webhook global) |
Defaults no config/notify.php:
'websocket' => [ 'config_id' => env('NOTIFY_WEBSOCKET_CONFIG_ID'), // usado quando não passa ->configId() 'tags' => [], // mescladas com as da notificação ],
Precedência: ->configId() da notificação sobrescreve o config_id do config; as tags
da notificação são somadas às notify.websocket.tags (sem duplicar).
Exemplos:
use RiseTechApps\Notify\Message\NotifyWebSocket; // Canal público (new NotifyWebSocket) ->channel('notifications') ->event('OrderUpdated') ->data(['order_id' => '1234']) ->tag('pedidos'); // Canal privado (por usuário) (new NotifyWebSocket) ->channel("user.{$userId}") ->event('NewMessage') ->data(['from' => 'Ana', 'preview' => 'Bom dia!']) ->private(); // Canal de presença (new NotifyWebSocket) ->channel('sala-suporte') ->event('UserJoined') ->data(['user' => 'João']) ->presence(); // Credencial específica + múltiplas tags (new NotifyWebSocket) ->channel('notifications') ->event('Broadcast') ->data(['msg' => 'Manutenção às 22h']) ->configId('Pusher Produção') ->tag(['avisos', 'sistema']);
Envio on-demand (sem model):
use Illuminate\Support\Facades\Notification; Notification::route('websocket', 'notifications')->notify(new PedidoAtualizado());
Ciclo de vida completo do WebSocket:
| Etapa | Como |
|---|---|
| Enviar | $user->notify(...) ou Notification::route('websocket', $canal)->notify(...) |
| Acompanhar status | eventos NotifySentEvent e NotifyWebhookEvent — ver Eventos |
| Listar histórico | NotifyQuery::server()->websocket()->tag('pedidos')->status('sent')->get() |
| Detalhe + timeline | NotifyQuery::server()->websocket('uuid')->get() |
Cancelar (se created) |
NotifyQuery::server()->websocket('uuid')->cancel() |
Detalhes em Consultas de status → WebSocket.
Webhook
Faz uma requisição HTTP a um endpoint de destino (ERP, sistema externo etc.). O servidor
dispara a chamada e devolve o status pelo callback. Não confunda o url (destino da
requisição) com o webhookUrl (seu callback de status).
use RiseTechApps\Notify\Message\NotifyWebhook; class PedidoCriado extends Notification { public function via($notifiable): array { return ['notify.webhook']; } public function toNotifyWebhook($notifiable): NotifyWebhook { return (new NotifyWebhook) ->url('https://erp.empresa.com/api/eventos') ->method('POST') ->payload(['evento' => 'pedido_criado', 'id' => 1234]) ->bearerAuth('token-secreto') ->timeout(15) ->tag('erp'); // opcional, agrupa/filtra } }
| Método | Descrição |
|---|---|
->url(string) |
URL de destino. Máx: 2048 chars (se omitido, usa o default_url da config) |
->method(string) |
POST | GET | PUT | PATCH. Default: POST |
->payload(array) |
Body da requisição |
->header(key, value) |
Header customizado |
->headers(array) |
Múltiplos headers de uma vez |
->bearerAuth(token) |
Autenticação Bearer |
->basicAuth(user, pass) |
Autenticação Basic |
->apiKeyAuth(token) |
Autenticação por API Key |
->hmacAuth() |
Assinatura HMAC |
->timeout(int) |
Timeout em segundos. Min: 1, Máx: 60 |
->tag(string|array) |
Tag(s) para agrupar/filtrar no histórico (acumula) |
->configId(string) |
Credencial Webhook (UUID ou label) |
->webhookUrl(string) |
URL de callback de status (default: notify.webhook global) |
O canal webhook não tem bloco de defaults em
config/notify.php(a chavenotify.webhookjá é o callback global de status). Definatag/configIdpor mensagem.
Notas de autenticação e entrega:
bearerAuth/apiKeyAuthexigem o token;basicAuthexige usuário+senha. Para API Key, o header é oapi_key_headerda config (defaultX-API-KEY).hmacAuth()assina o payload com osecretda config (SHA-256) e enviaX-Notify-Signature+X-Hub-Signature-256— o segredo não vai no request.- Se a config tiver
inject_metadata: true(default), o servidor injeta um bloco_notify(timestamp,source,channel) no payload entregue ao destino. - Sucesso = destino respondeu
2xx.4xx(exceto 408/429) = falha permanente (sem retry);5xx/timeout/conexão/408/429 = retry com backoff + failover (se houver mais endpoints).
Envio on-demand (sem model):
use Illuminate\Support\Facades\Notification; Notification::route('webhook', 'https://erp.empresa.com/api/eventos')->notify(new PedidoCriado());
Ciclo de vida completo do Webhook:
| Etapa | Como |
|---|---|
| Enviar | $user->notify(...) ou Notification::route('webhook', $url)->notify(...) |
| Acompanhar status | eventos NotifySentEvent e NotifyWebhookEvent — ver Eventos |
| Listar histórico | NotifyQuery::server()->webhook()->tag('erp')->status('sent')->get() |
| Detalhe + timeline | NotifyQuery::server()->webhook('uuid')->get() |
Cancelar (se created) |
NotifyQuery::server()->webhook('uuid')->cancel() |
Detalhes em Consultas de status → Webhook.
Config do driver generic (credenciais/defaults no servidor):
NotifyQuery::server()->webhook()->config() ->driver('generic') ->label('ERP Empresa') ->defaultUrl('https://erp.empresa.com/api/eventos') ->authType('bearer') ->authToken('token-secreto') ->timeout(15) ->injectMetadata(true) ->asDefault() ->save();
Credenciais por driver
Para criar/atualizar/remover configurações, use
NotifyQuery::server()->{canal}()->config()— veja Configurações de driver.
Fonte de verdade:
NotifyQuery::server()->drivers()(lido dinamicamente do servidor). A tabela abaixo reflete o contrato atual; odefault_driverde cada canal está marcado.
| Canal | Driver | Credenciais (credential_fields) |
|---|---|---|
sms |
mobizon (padrão) |
key, api_server |
sms |
clicksend |
username, api_key, from |
sms |
twilio |
sid, token, from |
email |
smtp (padrão) |
(nenhuma — usa config/mail.php) |
email |
ses |
key, secret, region |
email |
mailgun |
domain, secret, endpoint |
email |
sendgrid |
api_key |
email |
postmark |
token |
email |
resend |
api_key |
push |
fcm |
credentials_json (objeto do Service Account) — ->credentialsFile() lê o .json e embeda; ou ->credentialsJson() inline |
apns |
apns |
key_id, team_id, bundle_id, key_path, production |
telegram |
telegram |
bot_token |
slack |
slack |
bot_token, webhook_url, default_channel |
discord |
webhook |
webhook_url, username, avatar_url |
teams |
webhook |
webhook_url, theme_color |
websocket |
pusher |
app_id, key, secret, cluster, host, port, scheme |
webhook |
generic |
default_url, timeout, auth_type, auth_token, auth_user, auth_password, api_key_header, secret, inject_metadata |
Usando uma config específica no envio
Todos os canais e o NotifyCampaignBuilder aceitam ->configId(string) para sobrescrever a config padrão:
// Notificação individual public function toNotifySms($notifiable): NotifySms { return (new NotifySms) ->to($notifiable->phone) ->content('Mensagem') ->configId('uuid-da-config-twilio-backup'); } // Campanha NotifyCampaignBuilder::sms() ->name('Promo') ->content('Olá {name}!') ->contacts([...]) ->configId('uuid-da-config') ->send();
Campanhas em massa
Campanhas não usam o sistema de notificações do Laravel — são disparadas diretamente pela classe NotifyCampaignBuilder. Suportam até 10.000 contatos por disparo com rate limiting configurável.
Campanha SMS
use RiseTechApps\Notify\NotifyCampaignBuilder; $campaign = NotifyCampaignBuilder::sms() ->name('Promo Dia das Mães') ->content('Olá {name}! 30% OFF hoje. Use: {cupom}. Loja.com') ->tag(['promo', 'maes']) ->contacts([ ['phone' => '5521981425950', 'name' => 'João', 'extra_data' => ['cupom' => 'MAES30']], ['phone' => '5521969014860', 'name' => 'Maria', 'extra_data' => ['cupom' => 'MAES30']], ]) ->webhookUrl('https://sua-app.com/notify/webhook/campaign') ->ratePerMinute(100) ->send(); // $campaign é o array cru da resposta do servidor (campaign_id, status, ...)
| Método | Descrição |
|---|---|
->name(string) |
Nome da campanha |
->content(string) |
Texto do SMS. Máx: 160 chars. Variáveis (chave simples): {name}, {phone} + chaves de extra_data |
->tag(string|array) |
Tag(s) propagada(s) para cada notificação |
->contacts(array) |
Array direto: [['phone' => '...', 'name' => '...', 'extra_data' => [...]]] |
->fromQuery(Builder, col, nameCol?, extraCols?) |
Via query Eloquent — processada em chunks de 500 |
->fromCollection(Collection, col, nameCol?, extraCols?) |
Via Collection Laravel |
->configId(string) |
UUID da config SMS |
->webhookUrl(string) |
URL de callback de progresso |
->ratePerMinute(int) |
Envios por minuto. Min: 1, Máx: 600. Default: 60 |
->scheduledAt(string) |
Agendamento: '2026-06-01 08:00:00' |
->send() |
Envia e retorna o array da resposta do servidor |
O servidor não aceita mais
from/sender ID em SMS — o remetente vem da credencial (config).
Campanha Email
use RiseTechApps\Notify\NotifyCampaignBuilder; $campaign = NotifyCampaignBuilder::email() ->name('Newsletter Março 2026') ->subject('Novidades de Março!') ->line('Confira as novidades deste mês.') ->action('https://app.com/blog', 'Ler mais') ->from('news@app.com', 'Minha App') ->tag('newsletter') ->contacts([ ['email' => 'joao@email.com', 'name' => 'João'], ['email' => 'maria@email.com', 'name' => 'Maria'], ]) ->webhookUrl('https://sua-app.com/notify/webhook/campaign') ->scheduledAt('2026-03-15 09:00:00') ->send();
Email usa placeholder de chave dupla:
{{name}}+ chaves deextra_data(ex.:{{cupom}}). Se->from()não for informado, cai paraconfig('notify.mail.from.address'|'name').
| Método | Descrição |
|---|---|
->name(string) |
Nome da campanha |
->subject(string) |
Assunto do email |
->subjectMessage(string) |
Subtítulo / preview |
->line(string) |
Linha de texto principal |
->lineHeader(string) |
Linha no cabeçalho (chamadas múltiplas) |
->lineFooter(string) |
Linha no rodapé |
->action(url, text) |
Botão de call-to-action |
->theme(string) |
Tema do template. Default: default |
->signature(string) |
Assinatura |
->from(email, name?) |
Remetente (default: config('notify.mail.from.*')) |
->tag(string|array) |
Tag(s) propagada(s) para cada notificação |
->addTable(EmailTable|array) |
Tabela no corpo: ['headers' => [], 'rows' => [[]]] |
->addList(type, items) |
Lista ordered ou unordered |
->contacts(array) |
Array direto: [['email' => '...', 'name' => '...', 'extra_data' => [...]]] |
->fromQuery(Builder, col, nameCol?, extraCols?) |
Via query Eloquent |
->fromCollection(Collection, col, nameCol?, extraCols?) |
Via Collection |
->configId(string) |
UUID da config de email |
->webhookUrl(string) |
URL de callback |
->ratePerMinute(int) |
Envios por minuto. Default: 60 |
->scheduledAt(string) |
Agendamento futuro |
->send() |
Envia e retorna o array da resposta do servidor |
Fontes de contatos
Array direto:
->contacts([ ['phone' => '5511999887766', 'name' => 'João'], // SMS ['email' => 'joao@email.com', 'name' => 'João', 'extra_data' => [...]], // Email ])
Via query Eloquent — não carrega todos os registros em memória, processa em chunks de 500:
->fromQuery( User::where('active', true)->where('accepts_sms', true), contactColumn: 'phone', // coluna com telefone ou email no banco nameColumn: 'name', // opcional extraColumns: ['cupom' => 'coupon'] // viram extra_data (placeholders); ['cupom'] mantém o nome )
Via Collection:
->fromCollection($users, contactColumn: 'email', nameColumn: 'full_name', extraColumns: ['cidade'])
extraColumnsaceita lista (['cidade']→ placeholder{cidade}/{{cidade}}com o mesmo nome) ou mapa (['cupom' => 'coupon_code']→ placeholder{cupom}lendo a colunacoupon_code).
Estado e rastreamento
O pacote não persiste nada localmente — não há tabelas, models nem migrations. Todo o estado (status, eventos, contadores de campanha) vive no servidor.
Para acompanhar uma notificação você tem dois caminhos:
- Tempo real / sob demanda → consulte o servidor com
NotifyQuery::server()(ver abaixo). - Push do servidor → receba os callbacks no webhook, que são
repassados como eventos Laravel (
NotifyWebhookEvent/NotifyCampaignWebhookEvent). Persista o que quiser no seu próprio banco a partir do listener.
Webhook receiver
O package inclui NotifyWebhookController que recebe os callbacks do servidor e os
repassa como eventos Laravel — ele não grava nada. Escute os eventos para reagir.
Notificação individual → dispara NotifyWebhookEvent. Payload típico recebido:
{
"event": "sent",
"notification_id": "uuid-do-servidor",
"provider_id": "11",
"status": "send",
"type": "telegram"
}
Campanha → dispara NotifyCampaignWebhookEvent. Payload típico:
{
"campaign_id": "uuid-da-campanha-no-servidor",
"status": "processing",
"total": 1000,
"sent": 450,
"failed": 12,
"contact_updates": [
{ "contact": "joao@email.com", "status": "sent" },
{ "contact": "erro@email.com", "status": "failed", "error": "Mailbox full" }
]
}
Exemplo de listener:
use RiseTechApps\Notify\Events\NotifyWebhookEvent; public function handle(NotifyWebhookEvent $event): void { // $event->event, $event->notificationId, $event->providerId, // $event->status, $event->type, $event->payload (array cru completo) match ($event->status) { 'delivered' => /* ... */ null, 'error' => /* ... */ null, default => null, }; }
Consultas de status
Todas as consultas vão ao servidor (HTTP com a NOTIFY_SERVICE_KEY), via NotifyQuery::server().
SMS (endpoints dedicados)
use RiseTechApps\Notify\NotifyQuery; // Listar (filtra por tag/status) — retorna o paginador do servidor $result = NotifyQuery::server()->sms() ->tag('promo') // ou ->tag(['promo', 'junho']) — contém qualquer uma ->status('delivered') // created|sending|send|delivered|error|cancelled ->perPage(50) // 1–100, default 20 ->page(1) ->get(); $result['data']; // array de SMS $result['total']; // total, current_page, last_page, per_page // Detalhe + timeline de eventos $sms = NotifyQuery::server()->sms('server-uuid')->get(); $sms['status']; // status atual $sms['events']; // timeline: created → sending → send → delivered // Cancelar — só funciona enquanto o status for `created` (ainda na fila) $res = NotifyQuery::server()->sms('server-uuid')->cancel(); // ->sms()->cancel('server-uuid') também funciona // ['status' => bool, 'message' => string|null, 'current_status' => string|null, 'http' => int] // http 200 = cancelado · 409 = já enviando/enviado (veja current_status) · 404 = não é seu/inexistente
Atenção ao filtro de tag: conforme o contrato do servidor, sem informar
taga listagem retorna apenas os SMS SEM tag. Informe->tag(...)para trazer os que contêm a(s) tag(s) desejada(s).
Email (endpoints dedicados)
use RiseTechApps\Notify\NotifyQuery; // Listar (filtra por tag/status) — retorna o paginador do servidor $result = NotifyQuery::server()->mail() ->tag('pedido') // ou ->tag(['pedido', 'transacional']) — contém qualquer uma ->status('delivered') // created|sending|sent|delivered|ready|error|cancelled ->perPage(50) // 1–100, default 20 ->page(1) ->get(); // Detalhe + timeline de eventos $mail = NotifyQuery::server()->mail('server-uuid')->get(); $mail['events']; // timeline: created → sending → sent → delivered/ready/error // Cancelar — só funciona enquanto o status for `created` (ainda na fila) $res = NotifyQuery::server()->mail('server-uuid')->cancel(); // ->mail()->cancel('server-uuid') também funciona // ['status' => bool, 'message' => string|null, 'notification_id' => string|null, 'current_status' => string|null, 'http' => int] // http 200 = cancelado · 409 = já enviando/finalizado (veja current_status) · 404 = não é seu/inexistente
Mesmo comportamento do SMS no filtro de tag: sem
tag()a listagem traz só os e-mails SEM tag. Informe->tag(...)para trazer os que contêm a(s) tag(s).
Push (endpoints dedicados)
use RiseTechApps\Notify\NotifyQuery; // Listar (filtra por tag/status) — retorna o paginador do servidor $result = NotifyQuery::server()->push() ->tag('promo') // string única (no push a tag não é array) ->status('sent') // created|sending|sent|error|failed|cancelled ->perPage(50) // 1–100, default 20 ->page(1) ->get(); // Detalhe + timeline de eventos $push = NotifyQuery::server()->push('server-uuid')->get(); $push['events']; // timeline: created → sending → sent · error · failed // Cancelar — só funciona enquanto o status for `created` (ainda na fila) $res = NotifyQuery::server()->push('server-uuid')->cancel(); // ->push()->cancel('server-uuid') também funciona // ['status' => bool, 'message' => string|null, 'current_status' => string|null, 'http' => int] // http 200 = cancelado · 409 = já enviando/enviado (veja current_status) · 404 = não é seu/inexistente
Mesmo comportamento de tag dos outros canais: sem
tag()a listagem traz só os push SEM tag.
APNS (endpoints dedicados)
use RiseTechApps\Notify\NotifyQuery; // Listar (filtra por tag/status) — retorna o paginador do servidor $result = NotifyQuery::server()->apns() ->tag('entregas') // ou ->tag(['entregas', 'junho']) ->status('sent') // created|sending|sent|error|failed|cancelled ->perPage(50) // 1–100, default 20 ->page(1) ->get(); // Detalhe + timeline de eventos $apns = NotifyQuery::server()->apns('server-uuid')->get(); $apns['events']; // timeline: created → sending → sent · error · failed // Cancelar — só funciona enquanto o status for `created` (ainda na fila) $res = NotifyQuery::server()->apns('server-uuid')->cancel(); // ->apns()->cancel('server-uuid') também funciona // ['status' => bool, 'message' => string|null, 'current_status' => string|null, 'http' => int] // http 200 = cancelado · 409 = já enviando/enviado (veja current_status) · 404 = não é seu/inexistente
Mesmo comportamento de tag dos outros canais: sem
tag()a listagem traz só os envios SEM tag.
Telegram (endpoints dedicados)
use RiseTechApps\Notify\NotifyQuery; // Listar (filtra por tag/status) — retorna o paginador do servidor $result = NotifyQuery::server()->telegram() ->tag('pedidos') // ou ->tag(['pedidos', 'junho']) ->status('sent') // created|sending|sent|error|failed|cancelled ->perPage(50) // 1–100, default 20 ->page(1) ->get(); // Detalhe + timeline de eventos $msg = NotifyQuery::server()->telegram('server-uuid')->get(); $msg['events']; // timeline: created → sending → sent · error · failed // Cancelar — só funciona enquanto o status for `created` (ainda na fila) $res = NotifyQuery::server()->telegram('server-uuid')->cancel(); // ->telegram()->cancel('server-uuid') também funciona // ['status' => bool, 'message' => string|null, 'current_status' => string|null, 'http' => int] // http 200 = cancelado · 409 = já enviando/enviado (veja current_status) · 404 = não é seu/inexistente // Editar / apagar / fixar uma mensagem já enviada (síncrono, só o notification_id) NotifyQuery::server()->telegram('server-uuid')->edit('Texto editado'); NotifyQuery::server()->telegram('server-uuid')->editCaption('Nova legenda'); NotifyQuery::server()->telegram('server-uuid')->delete(); NotifyQuery::server()->telegram('server-uuid')->pin(disableNotification: true); // ['status' => bool, 'message' => string|null, 'http' => int]
Mais detalhes em Telegram → Gerenciar mensagens enviadas.
Mesmo comportamento de tag dos outros canais: sem
tag()a listagem traz só os envios SEM tag.
Slack (endpoints dedicados)
use RiseTechApps\Notify\NotifyQuery; // Listar (filtra por tag/status) — retorna o paginador do servidor $result = NotifyQuery::server()->slack() ->tag('deploys') // ou ->tag(['deploys', 'junho']) ->status('sent') // created|sending|sent|error|failed|cancelled ->perPage(50) // 1–100, default 20 ->page(1) ->get(); // Detalhe + timeline de eventos $slack = NotifyQuery::server()->slack('server-uuid')->get(); $slack['events']; // timeline: created → sending → sent · error · failed // Cancelar — só funciona enquanto o status for `created` (ainda na fila) $res = NotifyQuery::server()->slack('server-uuid')->cancel(); // ->slack()->cancel('server-uuid') também funciona // ['status' => bool, 'message' => string|null, 'current_status' => string|null, 'http' => int] // http 200 = cancelado · 409 = já enviando/enviado (veja current_status) · 404 = não é seu/inexistente // Editar / apagar uma mensagem já enviada (síncrono, só Bot Token) NotifyQuery::server()->slack('server-uuid')->edit('Texto editado ✅'); NotifyQuery::server()->slack('server-uuid')->delete(); // ['status' => bool, 'message' => string|null, 'http' => int]
Mesmo comportamento de tag dos outros canais: sem
tag()a listagem traz só os envios SEM tag.
Discord (endpoints dedicados)
use RiseTechApps\Notify\NotifyQuery; // Listar (filtra por tag/status) — retorna o paginador do servidor $result = NotifyQuery::server()->discord() ->tag('cadastros') // ou ->tag(['cadastros', 'junho']) ->status('sent') // created|sending|sent|error|failed|cancelled ->perPage(50) // 1–100, default 20 ->page(1) ->get(); // Detalhe + timeline de eventos $discord = NotifyQuery::server()->discord('server-uuid')->get(); $discord['events']; // timeline: created → sending → sent · error · failed // Cancelar — só funciona enquanto o status for `created` (ainda na fila) $res = NotifyQuery::server()->discord('server-uuid')->cancel(); // ->discord()->cancel('server-uuid') também funciona // ['status' => bool, 'message' => string|null, 'current_status' => string|null, 'http' => int] // http 200 = cancelado · 409 = já enviando/enviado (veja current_status) · 404 = não é seu/inexistente // Editar / apagar uma mensagem já enviada (síncrono) NotifyQuery::server()->discord('server-uuid')->edit('Incidente resolvido ✅'); NotifyQuery::server()->discord('server-uuid')->delete(); // ['status' => bool, 'message' => string|null, 'http' => int]
Mesmo comportamento de tag dos outros canais: sem
tag()a listagem traz só os envios SEM tag.
Teams (endpoints dedicados)
use RiseTechApps\Notify\NotifyQuery; // Listar (filtra por tag/status) — retorna o paginador do servidor $result = NotifyQuery::server()->teams() ->tag('relatorios') // ou ->tag(['relatorios', 'junho']) ->status('sent') // created|sending|sent|error|failed|cancelled ->perPage(50) // 1–100, default 20 ->page(1) ->get(); // Detalhe + timeline de eventos $teams = NotifyQuery::server()->teams('server-uuid')->get(); $teams['events']; // timeline: created → sending → sent · error · failed // Cancelar — só funciona enquanto o status for `created` (ainda na fila) $res = NotifyQuery::server()->teams('server-uuid')->cancel(); // ->teams()->cancel('server-uuid') também funciona // ['status' => bool, 'message' => string|null, 'current_status' => string|null, 'http' => int] // http 200 = cancelado · 409 = já enviando/enviado (veja current_status) · 404 = não é seu/inexistente
Mesmo comportamento de tag dos outros canais: sem
tag()a listagem traz só os envios SEM tag.
WebSocket (endpoints dedicados)
use RiseTechApps\Notify\NotifyQuery; // Listar (filtra por tag/status) — retorna o paginador do servidor $result = NotifyQuery::server()->websocket() ->tag('pedidos') // ou ->tag(['pedidos', 'junho']) ->status('sent') // created|sending|sent|error|failed|cancelled ->perPage(50) // 1–100, default 20 ->page(1) ->get(); // Detalhe + timeline de eventos $ws = NotifyQuery::server()->websocket('server-uuid')->get(); $ws['events']; // timeline: created → sending → sent · error · failed // Cancelar — só funciona enquanto o status for `created` (ainda na fila) $res = NotifyQuery::server()->websocket('server-uuid')->cancel(); // ->websocket()->cancel('server-uuid') também funciona // ['status' => bool, 'message' => string|null, 'current_status' => string|null, 'http' => int] // http 200 = cancelado · 409 = já enviando/enviado (veja current_status) · 404 = não é seu/inexistente
Mesmo comportamento de tag dos outros canais: sem
tag()a listagem traz só os envios SEM tag.
Webhook (endpoints dedicados)
use RiseTechApps\Notify\NotifyQuery; // Listar (filtra por tag/status) — retorna o paginador do servidor $result = NotifyQuery::server()->webhook() ->tag('erp') // ou ->tag(['erp', 'junho']) ->status('sent') // created|sending|sent|error|cancelled ->perPage(50) // 1–100, default 20 ->page(1) ->get(); // Detalhe + timeline de eventos $wh = NotifyQuery::server()->webhook('server-uuid')->get(); $wh['events']; // timeline: created → sending → sent (metadata.status_code) · error // Cancelar — só funciona enquanto o status for `created` (ainda na fila) $res = NotifyQuery::server()->webhook('server-uuid')->cancel(); // ->webhook()->cancel('server-uuid') também funciona // ['status' => bool, 'message' => string|null, 'notification_id' => string|null, 'current_status' => string|null, 'http' => int] // http 200 = cancelado · 409 = já enviando/enviado (veja current_status) · 404 = não é seu/inexistente
Mesmo comportamento de tag dos outros canais: sem
tag()a listagem traz só os envios SEM tag.
Notificações (genérico, todos os canais)
use RiseTechApps\Notify\NotifyQuery; // ── Notificações ───────────────────────────────────────────────────────────── // Listar — retorna ['data' => [...], 'meta' => [...]] $result = NotifyQuery::server()->notifications() ->channel('sms') // sms|email|push|apns|telegram|slack|discord|teams|websocket|webhook ->status('send') // created|sending|send|delivered|ready|error ->from('2026-01-01') ->to('2026-03-31') ->campaignId('uuid') // filtra por campanha ->perPage(50) // máx: 100, default: 25 ->page(2) ->get(); $result['data']; // array de notificações $result['meta']; // total, per_page, current_page, last_page // Detalhes + timeline de eventos de uma notificação $notification = NotifyQuery::server()->notification('server-uuid'); // Inclui todos os campos + array 'events' com a timeline completa // Só a timeline (ideal para polling) $events = NotifyQuery::server()->notificationEvents('server-uuid'); $events['current_status']; // status atual $events['events']; // array de eventos com timestamps // ── Campanhas ───────────────────────────────────────────────────────────────── // Endpoints por canal: /api/v1/send/campaigns/{sms|mail}. Informe o canal no 1º arg. // Listar campanhas $result = NotifyQuery::server()->campaigns('mail') // 'sms' | 'mail' (email vira mail) ->status('completed') // pending|processing|paused|completed|failed|cancelled ->tag('newsletter') // opcional ->from('2026-01-01') ->to('2026-03-31') ->perPage(25) ->get(); // Detalhes + progresso de uma campanha (canal + id) $campaign = NotifyQuery::server()->campaigns('mail', 'server-campaign-uuid')->get(); $campaign['progress_percent']; // 0 a 100 $campaign['pending_count']; // contatos ainda não processados // Cancelar campanha (pending/paused/processing) $res = NotifyQuery::server()->campaigns('sms', 'server-campaign-uuid')->cancel(); $res['status']; // true se cancelou $res['current_status']; // status atual em caso de 409 $res['http']; // 200 | 409 | 404 // Forma alternativa: ->campaigns('sms')->cancel('server-campaign-uuid') // Contatos da campanha — status e motivo da falha por destinatário (exige o id) $contacts = NotifyQuery::server()->campaigns('mail', 'server-campaign-uuid')->contacts() ->status('failed') // pending|sending|sent|failed|skipped (opcional) ->page(1) // opcional ->get(); $contacts['data']; // [['contact','name','status','error','sent_at'], ...] $contacts['pagination']; // ['current_page','per_page','total','last_page'] // Ideal para descobrir QUAIS contatos falharam e o porquê (ex.: 'Domínio sem MX').
Eventos Laravel
Como nada é persistido, os eventos são o principal ponto de integração. O pacote dispara dois grupos:
No envio (dentro do canal, a cada notificação individual):
use RiseTechApps\Notify\Events\NotifySendingEvent; // antes do POST ao servidor use RiseTechApps\Notify\Events\NotifySentEvent; // ($notifiable, $notification, $response, $channel) use RiseTechApps\Notify\Events\NotifyFailedEvent; // ($notifiable, $notification, $exception, $channel)
No webhook (callback do servidor, sem $notifiable/$notification):
use RiseTechApps\Notify\Events\NotifyWebhookEvent; // (event, notificationId, providerId, status, type, payload) use RiseTechApps\Notify\Events\NotifyCampaignWebhookEvent; // (campaignId, status, payload)
Registre listeners no EventServiceProvider:
protected $listen = [ \RiseTechApps\Notify\Events\NotifySentEvent::class => [ App\Listeners\LogNotificacaoEnviada::class, ], \RiseTechApps\Notify\Events\NotifyWebhookEvent::class => [ App\Listeners\AtualizarStatusNotificacao::class, ], \RiseTechApps\Notify\Events\NotifyCampaignWebhookEvent::class => [ App\Listeners\AtualizarProgressoCampanha::class, ], ];
Configurações de driver
Gerencie as credenciais de cada canal pelo próprio NotifyQuery, sem acessar o painel
do servidor. O ponto de entrada é por canal: NotifyQuery::server()->{canal}()->config().
O driver é informado com ->driver('twilio') e as credenciais por setters fluentes,
terminando em ->save().
use RiseTechApps\Notify\NotifyQuery;
Descobrir e listar
// O que dá pra cadastrar: cada canal com default_driver + credential_fields NotifyQuery::server()->drivers(); // Configs já cadastradas (só as ativas) NotifyQuery::server()->configurations(); // todas NotifyQuery::server()->configurations('sms'); // só SMS // Pelo recurso do canal (equivalente): sem id lista, com id detalha NotifyQuery::server()->sms()->config()->get(); // lista as configs de SMS NotifyQuery::server()->sms()->config('uuid')->get(); // detalhe (metadados + credential_keys)
Criar — SMS
Drivers: mobizon (padrão), clicksend, twilio.
// Mobizon (driver padrão do canal) NotifyQuery::server()->sms()->config() ->driver('mobizon') ->label('Mobizon') ->key('xxxxxxxxxxxxxxxx') ->apiServer('xx') // ex.: "61" (subdomínio da API Mobizon) ->asDefault() ->save(); // ClickSend NotifyQuery::server()->sms()->config() ->driver('clicksend') ->label('ClickSend Produção') ->username('user@empresa.com') ->apiKey('xxxxxxxxxxxxxxxx') ->from('Empresa') ->save(); // Twilio NotifyQuery::server()->sms()->config() ->driver('twilio') ->label('Twilio Principal') ->sid('ACxxxxxxxxxxxxxxxx') ->token('xxxxxxxxxxxxxxxx') ->from('+5511999999999') ->save();
Criar — Email
Drivers: smtp (padrão), ses, mailgun, sendgrid, postmark, resend.
// SMTP — NÃO tem credenciais próprias: usa o config/mail.php da sua app NotifyQuery::server()->mail()->config() ->driver('smtp') ->label('SMTP Produção') ->asDefault() ->save(); // Mailgun (domain / secret / endpoint) NotifyQuery::server()->mail()->config() ->driver('mailgun') ->label('Mailgun') ->domain('mg.empresa.com') ->secret('key-xxxxxxxxxxxxxxxx') ->endpoint('api.mailgun.net') // ou api.eu.mailgun.net ->save(); // Resend NotifyQuery::server()->mail()->config()->driver('resend')->label('Resend')->apiKey('re_xxxxxxxx')->save(); // SendGrid NotifyQuery::server()->mail()->config()->driver('sendgrid')->label('SendGrid')->apiKey('SG.xxxxxxxx')->save(); // Amazon SES NotifyQuery::server()->mail()->config() ->driver('ses') ->label('SES us-east-1') ->key('AKIAXXXXXXXXXXXXXXXX') ->secret('xxxxxxxxxxxxxxxx') ->region('us-east-1') ->save(); // Postmark NotifyQuery::server()->mail()->config()->driver('postmark')->label('Postmark')->token('xxxxxxxx')->save();
Criar — Push / APNS
// FCM — a partir do arquivo do Service Account: o client LÊ o arquivo e envia // o conteúdo (JSON parseado) em credentials_json (não o caminho) NotifyQuery::server()->push()->config() ->driver('fcm') ->label('FCM-PUSHER') ->credentialsFile(storage_path('app/google-services.json')) ->asDefault() ->save(); // FCM — Service Account inline (array ou string JSON), sem arquivo em disco NotifyQuery::server()->push()->config() ->driver('fcm') ->label('FCM Cliente X') ->credentialsJson($serviceAccountArray) // array OU string JSON ->save(); // APNS (iOS) NotifyQuery::server()->apns()->config() ->driver('apns') ->label('APNS iOS') ->keyPath('/path/to/AuthKey.p8') ->keyId('XXXXXXXXXX') ->teamId('XXXXXXXXXX') ->bundleId('com.empresa.app') ->production(true) ->save();
Criar — Mensageiros / WebSocket
// Telegram NotifyQuery::server()->telegram()->config() ->driver('telegram') ->label('Telegram Bot') ->botToken('123456789:AAxxxxxxxxxx') ->save(); // Slack NotifyQuery::server()->slack()->config() ->driver('slack') ->label('Slack Workspace') ->webhookUrl('https://hooks.slack.com/services/xxx/yyy/zzz') ->defaultChannel('#geral') ->save(); // Discord (driver "webhook": webhook_url / username / avatar_url) NotifyQuery::server()->discord()->config() ->driver('webhook') ->label('Discord Server') ->webhookUrl('https://discord.com/api/webhooks/xxx/yyy') ->defaultUsername('NotifyBot') ->avatarUrl('https://app.com/bot-avatar.png') ->save(); // Teams (driver "webhook": webhook_url / theme_color) NotifyQuery::server()->teams()->config() ->driver('webhook') ->label('Teams Canal Engenharia') ->webhookUrl('https://outlook.office.com/webhook/xxx') ->themeColor('0078D4') ->save(); // Pusher (WebSocket) NotifyQuery::server()->websocket()->config() ->driver('pusher') ->label('Pusher Produção') ->appId('xxxxxxxx') ->key('xxxxxxxxxxxxxxxx') ->secret('xxxxxxxxxxxxxxxx') ->cluster('mt1') ->save(); // Webhook (driver "generic") NotifyQuery::server()->webhook()->config() ->driver('generic') ->label('ERP Empresa') ->defaultUrl('https://erp.empresa.com/api/eventos') ->authType('bearer') ->authToken('token-secreto') ->timeout(15) ->injectMetadata(true) ->save();
O
config()está em todos os recursos de canal —server()->slack()->config()faz a credencial eserver()->slack()->get()traz o histórico, sem conflito. UseNotifyQuery::server()->drivers()para descobrir os drivers válidos e oscredential_fieldsde cada canal. O->driver()é obrigatório ao criar.
Detalhe, atualizar, definir padrão e remover
O mesmo ->save() cria ou atualiza, conforme o id: sem id (config()) faz cadastro
(POST); com id (config('uuid')) faz atualização (PUT, merge parcial das credenciais).
Na atualização o driver não muda.
// Detalhe (retorna só as chaves das credenciais, nunca os valores) NotifyQuery::server()->push()->config('config-uuid')->get(); // Atualizar — é o mesmo save(), só que com id. Merge parcial: só o que enviar muda. NotifyQuery::server()->mail()->config('config-uuid') ->label('Novo nome') ->password('nova-senha') // só a senha muda; o resto permanece ->asDefault() ->save(); // Definir como padrão do canal NotifyQuery::server()->sms()->config('config-uuid')->setDefault(); // Remover NotifyQuery::server()->sms()->config('config-uuid')->delete();
Aliases de canal (Telegram e Slack)
Cadastre nomes lógicos (ex.: equipe, geral, help) por configuração e, no envio,
mande só o nome — o servidor resolve para o destino real (chat_id no Telegram; ID/#canal
no Slack). Os aliases ficam por configuração (cada bot/workspace tem o seu mapa), e são
gerenciados via ->config('uuid')->channels():
use RiseTechApps\Notify\NotifyQuery; // Listar os aliases da config (mapa nome => destino) NotifyQuery::server()->telegram()->config('config-uuid')->channels()->all(); // Criar / atualizar (idempotente — se o nome existir, sobrescreve) NotifyQuery::server()->telegram()->config('config-uuid')->channels() ->set('equipe', '-1003142488245'); NotifyQuery::server()->slack()->config('config-uuid')->channels() ->set('equipe', 'C0BAX5KS8KE'); // Remover NotifyQuery::server()->telegram()->config('config-uuid')->channels()->remove('equipe');
No envio, basta usar o nome no alvo — nada de guardar chat_ids/IDs de canal:
// Telegram (new NotifyTelegram)->chatId('equipe')->message('Olá, equipe!'); // Slack (new NotifySlack)->channel('equipe')->message('Olá, equipe!');
Fallthrough: se o valor não for um alias cadastrado, é usado como está — continua valendo passar o destino direto (
->chatId('-1003142488245'),->channel('C0BAX5KS8KE')ou'#geral'/'@meucanal'). Edição/exclusão de mensagem no Telegram também funciona quando a mensagem foi enviada por alias (o servidor re-resolve). Odefault_channeldo Slack é sempre canal literal (não passa por alias).
Usando uma config específica em um envio
Depois de criar a config no servidor e ter o UUID dela, passe o config_id na mensagem:
// Notificação individual public function toNotifySms($notifiable): NotifySms { return (new NotifySms) ->to($notifiable->phone) ->content('Mensagem via Twilio') ->configId('uuid-da-config-twilio'); // sobrescreve a config padrão } // Campanha NotifyCampaignBuilder::sms() ->name('Promo') ->content('Olá {name}!') ->contacts($contacts) ->configId('uuid-da-config-twilio') // sobrescreve a config padrão ->send();
Licença
MIT — veja LICENSE.md para detalhes.
© 2026 Rise Tech Apps