Saltar para a documentação
Ruby

Endpoints

`webhooks.list`, `list_all`, `iterate`, `get`, `create`, `update`, `delete`, `rotate_secret`, `test`, `get_delivery` e `replay_delivery`, e os registos de entregas e de atividade.

Todos os métodos

webhooks.rb
endpoint = client.webhooks.create(  url: "https://acme.com/hooks/mail",  eventTypes: ["email.sent", "email.bounced"],  description: "Billing service") File.write(".openemail-webhook-secret", endpoint[:secret]) client.webhooks.listclient.webhooks.get(endpoint[:id])client.webhooks.update(endpoint[:id], enabled: false)client.webhooks.test(endpoint[:id])latest = client.webhooks.list_deliveries(endpoint[:id], limit: 1).items.firstclient.webhooks.get_delivery(endpoint[:id], latest[:id])client.webhooks.replay_delivery(endpoint[:id], latest[:id])rotated = client.webhooks.rotate_secret(endpoint[:id])File.write(".openemail-webhook-secret", rotated[:secret])client.webhooks.delete(endpoint[:id])

create é a ÚNICA altura em que o segredo é devolvido, além de rotate_secret. Uma leitura nunca o repete, por isso guarde-o antes de fazer mais alguma coisa. Omita eventTypes para o conjunto por omissão, todos os eventos email.* exceto email.replied. email.replied, domain.*, suppression.*, file.* e form.* só chegam a um endpoint quando este os nomeia.

rotate_secret não tem janela de sobreposição. O segredo antigo deixa de funcionar imediatamente, por isso coloque o novo em produção antes de rodar. Nunca é repetido automaticamente: uma repetição rodaria uma segunda vez e invalidaria o segredo que a primeira tentativa devolveu.

create também não é repetido, por isso uma falha de rede pode deixar um endpoint criado com um segredo que nunca viu. Verifique list antes de o criar de novo. Um espaço de trabalho comporta 10 endpoints por omissão, e o seguinte acima do limite dá um 422 workspace_limit_reached.

A que pode subscrever

OpenEmail::WEBHOOK_EVENTS é um Hash congelado com o nome de cada evento, para que possa apresentar a lista sem fazer um pedido, e webhooks.list_events devolve os mesmos nomes com uma frase para cada um, mais os limites a que um endpoint está sujeito. Os eventos são eventos da **caixa de correio**, não desta API: email.received dispara para o correio que chega à aplicação, e email.sent dispara para uma mensagem enviada pelo editor de mensagens. Subscrever não é o mesmo que observar o seu próprio tráfego de API.

file.uploaded dispara quando um ficheiro é colocado na página Ficheiros, e file.deleted quando um é eliminado. O seu data contém fileId, filename, mimeType, sizeBytes, direction, to, threadId, messageId, e uploadedAt ou deletedAt. to é o endereço a que o ficheiro pertence, ou nil para um ficheiro que pertence a todo o espaço de trabalho.

Os eventos de ficheiros não estão no conjunto por omissão, por isso um endpoint só os recebe quando os nomeia em eventTypes. Um endpoint limitado a alguns endereços só é avisado sobre os ficheiros desses endereços, pelo que um ficheiro carregado para todo o espaço de trabalho, com to a nil, não lhe é enviado.

form.submitted dispara quando alguém se inscreve através de um dos seus formulários, e form.confirmed quando uma inscrição pendente entra nas audiências, porque a pessoa abriu a ligação de confirmação ou porque a inscrição foi aprovada por si. O data de form.submitted contém formId, formName, submissionId, email, status, answers, audienceIds, sourceUrl e submittedAt. O data de form.confirmed contém formId, formName, submissionId, email, audienceIds, via, que é link ou approval, e confirmedAt.

Uma inscrição num formulário sem dupla confirmação envia form.submitted com status added e nenhum form.confirmed, por isso trate esse par como o momento em que alguém entra. Quem se inscreve de novo antes de confirmar mantém o mesmo submissionId, e form.submitted só volta a ser enviado quando as respostas mudaram. Os eventos de formulário não estão no conjunto por omissão, e um endpoint limitado a alguns endereços nunca os recebe, porque as inscrições pertencem a todo o espaço de trabalho.

Provar que funciona

webhook_test.rb
result = client.webhooks.test("whe_3f9c2a7b1e4d8f60a5c7b92d")puts result.dig(:delivery, :status), result.dig(:delivery, :responseCode) client.webhooks.iterate_deliveries("whe_3f9c2a7b1e4d8f60a5c7b92d") do |delivery|  puts "#{delivery[:eventType]} #{delivery[:status]} #{delivery[:responseCode]} #{delivery[:error]}"end

test envia por POST um evento email.sent sintético e assinado e espera que a tentativa termine. Retorna normalmente seja qual for a resposta do seu recetor, por isso baseie a lógica em delivery[:status] e não no facto de a chamada ter lançado uma exceção. Um 4xx é uma resposta útil: o URL está acessível e a recusa veio do seu próprio handler, muitas vezes da verificação da assinatura.

Um responseCode a nil significa que não houve resposta nenhuma (DNS, TLS, um timeout), o que é um facto diferente de uma resposta que disse 0. Cada linha leva attempt e maxAttempts, por isso várias linhas podem descrever um só evento: o mesmo eventId entre elas é o evento, e o número da tentativa é a tentativa. nextAttemptAt indica quando está prevista a repetição automática que se segue a uma linha.

Enviar de novo

webhook_replay.rb
detail = client.webhooks.get_delivery("whe_3f9c2a7b1e4d8f60a5c7b92d", "whd_8c1e4a7f2b9d3e6a0c5f1b28")p detail[:payload], detail[:responseBody], detail[:replayRefusal] replay = client.webhooks.replay_delivery("whe_3f9c2a7b1e4d8f60a5c7b92d", "whd_8c1e4a7f2b9d3e6a0c5f1b28")p replay.dig(:delivery, :status), replay.dig(:delivery, :responseCode)

Uma entrega que continua a falhar é tentada até 8 vezes: no momento, depois ao fim de 1 minuto, 5 minutos, 30 minutos, 2 horas, 5 horas, 10 horas e 10 horas, cerca de 27 horas e meia no total. Só se repete uma falha que valha a pena repetir: sem resposta, 408, 425, 429 ou um 5xx. Um reenvio manda outra vez o evento guardado com o mesmo id, type, createdAt e data, pelo que um recetor que descarta ids já tratados o trata como o evento que já conhece. Só a assinatura é nova.

  • replay_delivery envia um evento agora e devolve o que o seu servidor respondeu. Funciona também numa tentativa entregue e nunca é repetido. Antes de enviar, as repetições automáticas desse evento que ainda não começaram ficam em pausa: continuam canceladas se o reenvio for entregue e retomam no seu horário se falhar.
  • Se nesse momento estiver a ser enviada uma repetição automática do mesmo evento, replay_delivery não envia nada e lança um 409 retry_in_progress, e enquanto outro reenvio dele ainda estiver a ser enviado lança um 409 replay_in_progress, para que o seu recetor nunca receba duas cópias ao mesmo tempo, nem de dois reenvios feitos no mesmo instante. Espere alguns segundos e leia get_delivery, porque essa repetição ou esse reenvio pode entregá-lo. O reenvio é um evento de cada vez: nenhuma chamada reenvia todas as entregas falhadas.
  • Também lança um 409 para um endpoint desligado (webhook_disabled), um evento que o endpoint já não escuta (event_not_subscribed) ou já não cobre (event_out_of_scope), e uma tentativa sem evento guardado (delivery_not_replayable). get_delivery indica essa resposta de antemão como replayRefusal.

A gem nunca repete replay_delivery por iniciativa própria, porque uma repetição após uma resposta perdida enviaria o evento outra vez.

Parâmetros: webhooks.create

urlStringobrigatório
Para onde as entregas são feitas por POST. Apenas HTTPS, e o host não pode ser `localhost`, um nome `.localhost`, `.local` ou `.internal`, nem um literal de IP de loopback, privado, CGNAT ou link-local. Isto é um pedido do lado do servidor para um endereço que você fornece, por isso esses dão um 422 `invalid_webhook_url` em `url`. A verificação lê o hostname tal como foi escrito, e cada entrega volta a resolver o host e recusa enviar para um endereço num desses intervalos. As entregas nunca seguem redirecionamentos, por isso registe o endereço final. O que fica guardado é a serialização, feita pelo analisador de URLs, do que enviou, por isso `https://acme.com` é lido de volta como `https://acme.com/`.
eventTypesArray<String>
Que eventos chegam a este endpoint: quaisquer dos valores em `OpenEmail::WEBHOOK_EVENTS`. `create` limita o Array ao número de eventos que existem, por isso um a mais dá um 422 em `eventTypes`, e `update` não o limita. Só o comprimento é limitado, e um nome repetido é guardado e lido de volta exatamente como o enviou. Omitido ou vazio, é guardado como uma lista vazia, e é por isso que se lê de volta como `["*"]`, e significa todos os eventos `email.*` exceto `email.replied`, catorze hoje, e nunca as famílias de domínio, de supressão, de ficheiros ou de formulários. Uma família acrescentada mais tarde nunca chega a um endpoint que não a nomeou, para que uma integração não possa começar a receber uma forma que nunca viu por causa de um lançamento.
descriptionString
Uma etiqueta para o endpoint, com 200 caracteres no máximo, para que uma lista de webhooks se leia como nomes em vez de uma coluna de URLs. Omitida, é guardada e devolvida como nil.
addressAllowlistArray<String>
Endereços individuais sobre os quais este endpoint é avisado. Um evento é entregue quando o endereço a que diz respeito está nesta lista, ou quando o seu domínio está em `domainAllowlist`. Deixe ambos vazios e o endpoint é avisado sobre todos os endereços do espaço de trabalho. No máximo 50, e um endereço que não pertença a este espaço de trabalho dá um 422 `invalid_parameter`.
domainAllowlistArray<String>
Domínios inteiros sobre os quais este endpoint é avisado, incluindo os endereços que lhes forem adicionados mais tarde. Um domínio também traz os seus próprios eventos `domain.*`. No máximo 25.
api_keyString
Cria o endpoint com esta chave em vez da chave do cliente.

Resposta: o endpoint criado

Um Hash com chaves Symbol. get, list e update devolvem a mesma forma sem secret.

objectString
Sempre `webhook`, o mesmo discriminador que uma leitura simples devolve, porque o segredo é uma chave a mais na forma habitual e não um tipo de objeto próprio. Se `secret` está presente decide-se pelo método que chamou, não por este campo.
idString
O identificador do endpoint: `whe_` seguido de 24 caracteres hexadecimais. Todas as outras chamadas de webhook o recebem: `get`, `update`, `delete`, `rotate_secret`, `test`, `list_deliveries`, `list_all_deliveries`, `iterate_deliveries`, `get_delivery` e `replay_delivery`.
urlString
O endpoint tal como ficou guardado, depois de passar as verificações de HTTPS e de hosts bloqueados. É o URL analisado e novamente serializado, por isso compare com este valor e não com a String que enviou.
descriptionString or nil
A etiqueta que lhe deu, ou nil se não deu nenhuma. Um `update` que envie `description: nil` apaga-a.
eventTypesArray<String>
Os eventos subscritos, ou `["*"]` quando o endpoint não nomeou nenhum. `["*"]` é a forma como uma lista vazia guardada é apresentada na leitura e não pode ser enviado de volta, e representa os catorze eventos de mensagem e não o catálogo inteiro. `create` e `update` aceitam apenas os nomes literais dos eventos.
enabledBoolean
Indica se as entregas são tentadas. Um endpoint desativado é ignorado quando os eventos são despachados e mantém o seu segredo e o seu histórico de entregas. Aqui é sempre true, uma vez que só `update` aceita `enabled`.
disabledAtString or nil
Quando o servidor desligou o endpoint após 100 entregas falhadas seguidas. É nil enquanto está ligado, e quando foi você a desligá-lo.
disabledReasonString or nil
Porque é que o servidor o desligou. É nil sempre que `disabledAt` for nil.
consecutiveFailuresInteger
Entregas falhadas seguidas. Qualquer evento entregue repõe-no a 0, tal como `update` com `enabled: true`.
addressAllowlistArray<String>
Os endereços individuais sobre os quais este endpoint é avisado.
domainAllowlistArray<String>
Os domínios inteiros sobre os quais este endpoint é avisado. Com as duas listas vazias, é avisado sobre todos os endereços do espaço de trabalho.
lastDeliveryAtString or nil
Carimbo temporal ISO 8601 da última TENTATIVA de entrega, e não do último sucesso. É registado também depois de um POST falhado, por isso diz-lhe que o endpoint foi tentado, e `list_deliveries` diz-lhe como correu. É nil até à primeira tentativa, e por isso sempre nil em `create`.
createdAtString
Timestamp ISO 8601 de quando o endpoint foi registado. `list` devolve os endpoints do mais recente para o mais antigo por este campo.
secretString
A chave HMAC-SHA-256 que assina o `X-OpenEmail-Signature` de cada entrega: `whsec_` seguido de 43 caracteres base64url, e o que passa a `OpenEmail.verify_webhook_signature`, prefixo incluído. Devolvida por `create` e `rotate_secret` e por mais nada. Uma leitura nunca a repete, por isso guarde-a já. Um segredo perdido só pode ser substituído com `rotate_secret`, que invalida o antigo imediatamente.

Filtrar os registos

webhook_logs.rb
failed = client.webhooks.list_workspace_deliveries(status: "failed", since: Time.now - 86_400)p failed.items.map { |delivery| [delivery[:endpointId], delivery[:eventType], delivery[:responseCode]] } history = client.webhooks.list_activity("whe_3f9c2a7b1e4d8f60a5c7b92d")p history.items.map { |change| [change[:type], change.dig(:actor, :label)] }

list_deliveries lê um endpoint e list_workspace_deliveries todos os endpoints, ou os que endpoint_ids: nomeia, e ambos aceitam status:, since: e until:, os filtros do separador Entregas da consola. list_activity e list_workspace_activity leem o registo de auditoria: quem criou, alterou, ativou ou desativou, rodou, testou, reenviou ou removeu o quê. Cada um tem ao lado uma versão list_all_ e uma iterate_, e cada linha do registo do espaço de trabalho traz endpointId. webhooks.stats devolve os números por trás do separador Análises para um período à sua escolha.

since: e until: aceitam um Time, um DateTime ou um instante ISO 8601 como String, e uma Date do Ruby significa a meia-noite UTC desse dia. until é uma palavra-chave do Ruby, mas funciona como argumento nomeado como qualquer outro: list_deliveries(id, since: start, until: finish).