PushMesh
Entrar Criar conta
Abrir navegação das seções

Índice

Envio e confirmações por gRPC

A segunda porta da plataforma — envio de alta frequência como frames numa conexão só, e as confirmações de entrega como stream com ack, em vez de webhook. Mesmo contrato, mesma chave, outro transporte.

Envio e confirmações por gRPC

O REST resolve a maioria das integrações. Esta página é para quem está num dos dois extremos onde ele começa a doer:

  • Envio individual de alta frequência — transacional, um push por evento do seu sistema, milhares por minuto. Cada POST paga handshake, cabeçalho e JSON por viagem; no gRPC os pedidos viram frames numa conexão HTTP/2 que já está de pé.
  • Confirmações em fluxo contínuo — em vez de receber webhook (e manter um endpoint público, com retry e assinatura), você abre um stream e as confirmações de entrega descem por ele, com ack e retomada por cursor.

As duas portas falam o mesmo contrato do REST — mesmo corpo de disparo, mesmo envelope de erro, mesma chave de API — e são escolhas independentes: dá para enviar por gRPC e receber confirmação por webhook, ou o contrário.

O que o gRPC não muda: a entrega ao aparelho continua sendo APNs/FCM, e o recibo de entrega continua sendo a mesma prova HMAC de sempre. O ganho é no aceite e no transporte da confirmação — não existe mágica na última milha, e desconfie de quem prometer o contrário.

O endpoint

grpc.pushmesh.io:443   (TLS · HTTP/2 · sem proxy de terceiros na frente)

Autenticação em toda chamada, na metadata — a mesma chave do REST:

authorization: Basic pm_live_<kid>_<segredo>

A chave é revalidada a cada pedido dentro do stream: revogou no painel, o stream corta no pedido seguinte — não no reinício da conexão.

O contrato vive em proto/confirmacoes.proto no pacote pushmesh.confirmacoes.v1, e é congelável: campos só são adicionados, nunca renumerados ou renomeados. Peça o arquivo ao suporte ou copie da documentação — ele é a lei da porta.

As duas portas são independentes — leia isto primeiro

O modo grpc das configurações de confirmação controla só como as confirmações chegam até você. Ele NÃO é pré-requisito para enviar:

você querprecisa de
enviar por gRPCnada — só a chave de API na metadata; funciona hoje, com qualquer app
receber confirmações por gRPCligar o modo grpc nas confirmações do app

Qualquer combinação vale: enviar por gRPC e confirmar por webhook, enviar por REST e confirmar por stream — escolha por fluxo, não por pacote.

Comece em cinco minutos (testado de verdade)

O exemplo abaixo foi executado contra a produção antes de entrar nesta página — o aceite, o push no aparelho e o recibo de entrega aconteceram de fato. Você precisa do grpcurl (ou de qualquer cliente gRPC) e do arquivo confirmacoes.proto desta documentação, salvo no diretório atual.

python3 - <<'FIM' | grpcurl -proto confirmacoes.proto \
    -H 'authorization: Basic pm_live_SEU_KID_SEU_SEGREDO' \
    -d @ grpc.pushmesh.io:443 pushmesh.confirmacoes.v1.Envio/Enviar
import json
corpo = {
  "app_id": "SEU_APP_ID",
  "headings": {"pt-BR": "Enviado por gRPC"},
  "contents": {"pt-BR": "O mesmo corpo do REST, por outro cano"},
  "include_player_ids": ["ID_DO_PLAYER"],
}
print(json.dumps({"chave": "meu-pedido-1", "notificacao_json": json.dumps(corpo)}))
FIM

A resposta que volta, na mesma conexão:

{
  "chave": "meu-pedido-1",
  "notificationId": "01a05e29-587e-7e73-b3f4-3df343cfb659",
  "recipients": 1
}

Dois comportamentos para provar em seguida, porque são o coração do contrato:

  • Idempotência: rode o mesmo comando de novo, com a MESMA chave. Volta o mesmo notificationId — nenhum push duplicado. (Testado: voltou.)
  • A prova de entrega continua a mesma: consulte o disparo pelo REST (GET /api/v1/notifications/{id}) e veja recebidos contar quando o aparelho confirmar. O transporte mudou; a promessa não.

Porta 1 — Envio.Enviar: disparos como stream

service Envio {
  rpc Enviar(stream PedidoEnvio) returns (stream Aceite);
}

message PedidoEnvio {
  string chave = 1;             // idempotência (espelha o Idempotency-Key do REST)
  string notificacao_json = 2;  // o MESMO corpo do POST /api/v1/notifications
}

notificacao_json é, byte a byte, o corpo que você mandaria no POST /api/v1/notifications — a validação é a mesma, os erros são os mesmos. Você não aprende um segundo formato de disparo; aprende um segundo transporte.

Cada pedido volta como Aceite, na mesma conexão:

campoo que é
chavea sua, ecoada — é como você casa pedido e resposta
notification_ido id do disparo aceito
recipientso mesmo recipients da resposta REST
errors_jsono objeto errors do 2xx parcial — é aqui que vem invalid_player_ids
erro_jsonvazio = aceito; senão, o mesmo envelope de erro do REST, com explain
recusa_httptrue = a recusa foi do servidor (limite, validação) — trate como falha de transporte, nunca culpe o aparelho

Idempotência: mesma chave = mesmo disparo, como no REST. Reconectou e reenviou o lote em dúvida? O servidor absorve.

Teto: há um limite de streams simultâneos por app — uma chave vazada não abre conexões sem fim. Uma conexão com um stream é o desenho normal; abra mais streams só se medir que precisa.

Porta 2 — Confirmacoes.Fluxo: as confirmações descem por stream

Esta porta substitui o webhook para quem prefere conexão de saída a endpoint público. Exige ligar o modo nas configurações de confirmação do app (painel → Confirmações → modo grpc; os modos são consulta, webhook e grpc). Com o modo errado, a própria porta explica o que ligar — o erro ensina, como tudo aqui.

service Confirmacoes {
  rpc Fluxo(stream DoCliente) returns (stream Evento);
}

O protocolo em três passos:

  1. Abra o stream e mande Assinar{ app_id, cursor }. cursor = 0 começa da fila pendente; qualquer outro valor retoma do último entrega_id reconhecido.
  2. Receba Evento{ entrega_id, evento, payload_json, canal }. O payload_json é o mesmo corpo do webhook (com canal: "grpc"), e o entrega_id é o mesmo X-PM-Entrega-Id — se um dia você migrar de webhook para stream, a idempotência do seu lado continua valendo.
  3. Confirme com Ack{ entrega_id } — ou em lote, Ack{ entrega_ids: […] }. Só o ack marca o evento como entregue. Caiu a conexão? Reconecte com o último cursor reconhecido e o que não foi confirmado desce de novo.

Semântica at-least-once, como o webhook: prepare-se para receber um evento duas vezes na reconexão, e deduplique por entrega_id.

Eventos que descem pelo stream:

delivery.received · delivery.clicked · notification.completed · teste.ping

O teste.ping das configurações de confirmação também sai por aqui quando o modo é grpc — é como você prova o encanamento de ponta a ponta antes do primeiro disparo real.

Recebendo as confirmações em Node.js (porta 2)

Um consumidor completo do stream de confirmações, com ack — o esqueleto que a maioria das integrações usa:

const grpc = require('@grpc/grpc-js');
const protoLoader = require('@grpc/proto-loader');

const def = protoLoader.loadSync('confirmacoes.proto');
const pb = grpc.loadPackageDefinition(def).pushmesh.confirmacoes.v1;

const md = new grpc.Metadata();
md.set('authorization', 'Basic pm_live_SEU_KID_SEU_SEGREDO');

const cliente = new pb.Confirmacoes('grpc.pushmesh.io:443', grpc.credentials.createSsl());
const fluxo = cliente.Fluxo(md);

// 1. assina — cursor 0 começa da fila pendente; guarde o último ack para retomar
fluxo.write({ assinar: { app_id: 'SEU_APP_ID', cursor: 0 } });

fluxo.on('data', (ev) => {
  const corpo = JSON.parse(ev.payload_json);       // o MESMO corpo do webhook
  console.log(ev.evento, corpo);                   // delivery.received, delivery.clicked…
  // processe ANTES do ack: só o ack marca entregue — se você cair aqui,
  // o evento desce de novo na reconexão (at-least-once).
  fluxo.write({ ack: { entrega_id: ev.entrega_id } });
});

fluxo.on('error', (e) => {
  // reconecte com o último entrega_id reconhecido no lugar do cursor 0
  console.error('stream caiu — reconectar com cursor', e.message);
});

Antes do primeiro disparo real, prove o encanamento: o botão de teste das configurações de confirmação manda um teste.ping — ele desce por este mesmo stream quando o modo é grpc.

Detalhes de operação que valem saber

  • Keepalive HTTP/2 de 30 s dos dois lados: conexão morta é detectada sem tráfego. Configure o seu cliente para aceitar ping sem dados.
  • Os proxies intermediários são inimigos de stream longo — o nosso endpoint é direto, sem CDN na frente, com timeouts de 1 hora. Se houver proxy do SEU lado, dê a ele o mesmo respiro.
  • Cada confirmação entregue por aqui fica carimbada canal='grpc', e cada disparo aceito por aqui fica via='grpc' — a auditoria distingue a porta, para sempre.

Quando ficar no REST

Honestidade de contrato: se você dispara campanhas em lote algumas vezes por dia, o gRPC não te dá nada — o ganho aparece no envio individual de alta frequência e no fluxo contínuo de confirmações. Integração simples merece transporte simples.