Saltar para a documentação
PHP

Endpoints

`webhooks->list`, `listAll`, `iterate`, `get`, `create`, `update`, `delete`, `rotateSecret`, `test`, `getDelivery` e `replayDelivery`, e os registos de entregas e de atividade.

Todos os métodos

webhooks.php
use OpenEmail\Constants\WebhookEvents; $endpoint = $client->webhooks->create([    'url' => 'https://acme.com/hooks/mail',    'eventTypes' => [WebhookEvents::EMAIL_SENT, WebhookEvents::EMAIL_BOUNCED],    'description' => 'Billing service',]); file_put_contents('.openemail-webhook-secret', $endpoint['secret']); $client->webhooks->list();$client->webhooks->get($endpoint['id']);$client->webhooks->update($endpoint['id'], ['enabled' => false]);$client->webhooks->test($endpoint['id']); foreach ($client->webhooks->listDeliveries($endpoint['id'], limit: 1) as $latest) {    $client->webhooks->getDelivery($endpoint['id'], $latest['id']);    $client->webhooks->replayDelivery($endpoint['id'], $latest['id']);} $rotated = $client->webhooks->rotateSecret($endpoint['id']);file_put_contents('.openemail-webhook-secret', $rotated['secret']); $client->webhooks->delete($endpoint['id']);

create é a ÚNICA altura em que o segredo é devolvido, além de rotateSecret. 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.

list devolve uma OpenEmail\Result\Page, listAll devolve todos os endpoints num único array, e iterate devolve um Generator que entrega um endpoint de cada vez. create e update aceitam o corpo como um único array com os nomes da API, e cada endpoint volta como um array com chaves em camelCase.

rotateSecret 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\Constants\WebhookEvents nomeia cada evento como uma constante, e WebhookEvents::values() lista-os, para que possa apresentar a lista sem fazer um pedido. webhooks->listEvents devolve os mesmos nomes com uma etiqueta para cada um, mais os limites a que um endpoint está sujeito em maxEndpoints, maxAddresses e maxDomains. 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 null 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 null, 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.php
$result = $client->webhooks->test('whe_3f9c2a7b1e4d8f60a5c7b92d');echo $result['delivery']['status'], ' ', $result['delivery']['responseCode'] ?? 'no response', PHP_EOL; foreach ($client->webhooks->iterateDeliveries('whe_3f9c2a7b1e4d8f60a5c7b92d') as $delivery) {    echo $delivery['eventType'], ' ', $delivery['status'], ' ', $delivery['responseCode'] ?? '-', ' ', $delivery['error'] ?? '', PHP_EOL;}

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 $result['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 igual a null 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.php
$detail = $client->webhooks->getDelivery('whe_3f9c2a7b1e4d8f60a5c7b92d', 'whd_8c1e4a7f2b9d3e6a0c5f1b28');echo json_encode($detail['payload'], JSON_THROW_ON_ERROR), PHP_EOL;echo $detail['responseBody'] ?? 'no answer', ' ', $detail['replayRefusal']['code'] ?? 'replayable', PHP_EOL; $replay = $client->webhooks->replayDelivery('whe_3f9c2a7b1e4d8f60a5c7b92d', 'whd_8c1e4a7f2b9d3e6a0c5f1b28');echo $replay['delivery']['status'], ' ', $replay['delivery']['responseCode'] ?? 'no response', PHP_EOL;

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.

  • replayDelivery 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, replayDelivery 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 getDelivery, 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). Cada um é um ConflictException, e OpenEmail\Constants\WebhookReplayErrorCodes nomeia os códigos. getDelivery indica essa resposta de antemão como replayRefusal, null quando um reenvio avançaria e, caso contrário, um array com code e message.

O pacote nunca repete replayDelivery 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, de NAT de nível de operadora, link-local, multicast ou local único. 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
Que eventos chegam a este endpoint: quaisquer dos valores em `OpenEmail\Constants\WebhookEvents`. `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 null. Omita a chave em vez de passar null: o cliente envia um null tal como está, e `create` recusa-o com um 422.
addressAllowlistarray
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
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.
apiKeystring
Um argumento nomeado ao lado do array, e não uma chave dentro dele: cria o endpoint com esta chave de API em vez da chave do cliente.

Resposta: o endpoint criado

Um array com chaves em camelCase. 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`, `rotateSecret`, `test`, `listDeliveries`, `listAllDeliveries`, `iterateDeliveries`, `getDelivery` e `replayDelivery`.
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 null
A etiqueta que lhe deu, ou null se não deu nenhuma. Um `update` que envie `'description' => null` apaga-a.
eventTypesarray
Os eventos subscritos, ou `['*']` quando o endpoint não nomeou nenhum. `['*']` é como uma lista vazia armazenada é 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.
enabledbool
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 null
Quando o servidor desligou o endpoint após 100 entregas falhadas seguidas. É null enquanto está ligado, e quando foi você a desligá-lo.
disabledReasonstring or null
Porque é que o servidor o desligou. É null sempre que `disabledAt` for null.
consecutiveFailuresint
Entregas falhadas seguidas. Qualquer evento entregue repõe-no a 0, tal como `update` com `enabled` definido como true.
addressAllowlistarray
Os endereços individuais sobre os quais este endpoint é avisado.
domainAllowlistarray
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 null
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 `listDeliveries` diz-lhe como correu. É null até à primeira tentativa, e por isso sempre null 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::verifyWebhookSignature`, prefixo incluído. Devolvida por `create` e `rotateSecret` e por mais nada. Uma leitura nunca a repete, por isso guarde-a já. Um segredo perdido só pode ser substituído com `rotateSecret`, que invalida o antigo imediatamente.

Filtrar os registos

webhook_logs.php
$failed = $client->webhooks->listWorkspaceDeliveries(status: 'failed', since: new \DateTimeImmutable('-1 day')); foreach ($failed as $delivery) {    echo $delivery['endpointId'], ' ', $delivery['eventType'], ' ', $delivery['responseCode'] ?? '-', PHP_EOL;} $history = $client->webhooks->listActivity('whe_3f9c2a7b1e4d8f60a5c7b92d'); foreach ($history as $change) {    echo $change['type'], ' ', $change['actor']['label'] ?? 'OpenEmail', PHP_EOL;}

listDeliveries lê um endpoint e listWorkspaceDeliveries todos os endpoints, ou os que endpointIds: nomeia, como array ou como uma única string separada por vírgulas, e ambos aceitam status: (delivered ou failed), since: e until:, os filtros do separador Entregas da consola. listActivity e listWorkspaceActivity 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 listAll e uma iterate, como listAllDeliveries e iterateDeliveries, 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 DateTimeInterface ou uma string ISO 8601, e uma string com uma data simples significa a meia-noite UTC desse dia. until: tem de ser posterior a since:, caso contrário a chamada lança um InvalidRequestException com errorCode definido como invalid_parameter.