aronline / sdk
SDK oficial da API do AR Online (/v3)
Requires
- php: >=8.2
Requires (Dev)
- friendsofphp/php-cs-fixer: ^3.65
- phpstan/phpstan: ^2.0
- phpunit/phpunit: ^11.5
This package is not auto-updated.
Last update: 2026-08-20 09:47:05 UTC
README
Cliente oficial da API da AR Online para PHP.
O SDK fala com as duas superfícies da plataforma: a API legada do gateway, que
é a que está em produção hoje e concentra envio, status e comprovantes, e a
/v3, a API nova, para onde as funcionalidades estão sendo migradas.
Sobre a AR Online
A AR Online é uma plataforma brasileira de notificação eletrônica com validade jurídica. Uma única requisição dispara a notificação em até cinco canais, e cada etapa do percurso — envio, entrega e leitura — é registrada com carimbo do tempo emitido por uma Autoridade de Carimbo do Tempo da ICP-Brasil. Esse registro é o que dá à comunicação o valor de prova documental previsto na MP 2.200-2/2001, e é o que diferencia a plataforma de um serviço comum de disparo de mensagens.
Os canais disponíveis são:
| canal | o que é |
|---|---|
| AR-Email | e-mail com comprovação de entrega e de leitura |
| AR-SMS | mensagem de texto para o celular do destinatário |
| AR-WhatsApp | notificação por WhatsApp |
| AR-Voz | chamada telefônica automatizada |
| AR-Cartas | carta física registrada, enviada pelos Correios |
Você escolhe quais canais usar em cada envio. O processamento é assíncrono: a API confirma o recebimento na hora e devolve um identificador, que você usa depois para consultar o status de cada canal e baixar os comprovantes.
| Site | https://www.ar-online.com.br |
| Documentação da API | https://docs.ar-online.com.br |
| Suporte | suporte@ar-online.com.br · +55 (11) 4200-7766 |
Requisitos
- PHP 8.2 ou mais novo
- Extensões
ext-curleext-json - Nenhuma dependência de produção além dessas duas extensões
Instalação
composer require aronline/sdk
Autenticação
A plataforma tem duas superfícies de API, e cada uma usa uma credencial diferente. O SDK aceita as duas no mesmo cliente e envia cada uma no formato que a sua superfície espera.
Token do gateway (API legada)
É a credencial que você usa para enviar notificações e consultar status hoje.
Solicite em suporte@ar-online.com.br. No SDK, ela vai em legacyToken.
Token da API /v3
Solicite em suporte@ar-online.com.br. O token fica preso a uma entidade da sua conta, e é ela que define quais dados ele enxerga — se você precisa consultar mais de uma, peça um token para cada. O padrão é somente leitura.
O token tem prazo de validade. Token ausente, expirado ou revogado responde
401; se um token vazar, peça a revogação e ele deixa de ser aceito na chamada
seguinte.
A /v3 ainda não está publicada. O endereço
v3.ar-online.com.br, que é o padrão do SDK para essa superfície, entra no ar junto com ela — assim como a emissão de token por conta própria, na tela Gerar Token da documentação, com o mesmo usuário e senha do portal. Até lá, a parte da /v3 deste SDK serve para desenvolver contra um ambiente de teste, e é$client->legacy()que fala com a API em produção.
Primeiros passos
O envio de notificações é feito hoje pela API legada, exposta no SDK em
$client->legacy():
use ArOnline\Sdk\Client; use ArOnline\Sdk\Legacy\Model\CanalSms; use ArOnline\Sdk\Legacy\Model\EnvioRequest; $client = new Client(legacyToken: getenv('AR_GW_TOKEN')); $envio = $client->legacy()->send(new EnvioRequest( nameTo: 'João da Silva', subject: 'Notificação de vencimento', content: '<p>Prezado João, identificamos uma pendência em seu contrato.</p>', to: 'joao@exemplo.com', sms: new CanalSms(number: '11999998888'), )); echo 'notificação aceita: ', $envio->idEmail, PHP_EOL;
Guarde o idEmail: é com ele que você consulta o status de qualquer canal e
baixa os comprovantes.
$status = $client->legacy()->status()->email($envio->idEmail); echo $status->description, PHP_EOL; // Processado, Enviado, Entregue, Lido
Referência
Envio e acompanhamento ($client->legacy())
| método | o que faz |
|---|---|
legacy()->send(EnvioRequest) |
envia a notificação em um ou mais canais |
legacy()->status()->email(id) |
status do AR-Email |
legacy()->status()->sms(id) |
status do AR-SMS |
legacy()->status()->whatsapp(id) |
status do AR-WhatsApp |
legacy()->status()->voz(id) |
status do AR-Voz |
legacy()->status()->carta(id) |
status do AR-Cartas, com o rastreio dos Correios |
legacy()->status()->full(id) |
dados de perícia de todos os canais numa chamada |
legacy()->sendingProof(id) |
comprovante de envio em PDF |
legacy()->laudo(id) |
laudo pericial em PDF |
legacy()->finalizarRegua(id) |
encerra a régua de notificação do envio |
legacy()->templates()->list(GwTemplateType?) |
lista os modelos da sua entidade |
legacy()->templates()->get(id) |
busca um modelo |
legacy()->templates()->update(id, UpdateGwTemplate) |
edita nome e compartilhamento |
legacy()->templates()->deactivate(id) |
desativa um modelo |
legacy()->templates()->setStatus(id, ativo) |
ativa ou desativa um modelo |
Nas cinco rotas de status o id é sempre o uuid do e-mail da notificação — não existe um id por canal para você guardar.
O envio é multicanal: cada canal é um bloco opcional no corpo.
use ArOnline\Sdk\Legacy\Model\Anexo; use ArOnline\Sdk\Legacy\Model\CanalCarta; use ArOnline\Sdk\Legacy\Model\CanalSms; use ArOnline\Sdk\Legacy\Model\CanalVoz; use ArOnline\Sdk\Legacy\Model\CanalWhatsapp; use ArOnline\Sdk\Legacy\Model\EnvioRequest; use ArOnline\Sdk\Legacy\Model\SmsTypeSend; $client->legacy()->send(new EnvioRequest( nameTo: 'João da Silva', subject: 'Notificação de vencimento', content: '<p>Conteúdo em HTML.</p>', to: 'joao@exemplo.com', customID: 'contrato-4471', // sua referência, devolvida na consulta de status attachments: [new Anexo(name: 'contrato.pdf', base64: '…')], sms: new CanalSms( number: '11999998888', typeSend: SmsTypeSend::OnlyIfEmailFails, // Always manda em qualquer caso customMessage: 'Você recebeu um AR-Email. Acesse: {SHORT_LINK}', ), whatsapp: new CanalWhatsapp(number: '11999998888', variables: ['template' => 'aviso_01']), voz: new CanalVoz(number: '1133334444', template: 'aviso_voz'), carta: new CanalCarta(name: 'João da Silva', modelo: 'padrao'), ));
Comprovantes: o comprovante de envio chega em base64 dentro de um JSON e o SDK já o decodifica; o laudo pericial chega como PDF binário.
$comprovante = $client->legacy()->sendingProof($idEmail); if ($comprovante->pdf !== null) { file_put_contents('comprovante.pdf', $comprovante->pdf); } else { echo $comprovante->message, PHP_EOL; // ainda sem status de entrega } file_put_contents('laudo.pdf', $client->legacy()->laudo($idEmail));
As datas do legado
A API antiga tem quatro maneiras de dizer "isto ainda não aconteceu": texto
vazio, null, a chave sumindo da resposta e um objeto vazio. Às vezes duas
delas na mesma resposta. Em PHP um ?string não distingue null de chave
ausente, então os campos de instante são objetos LegacyDate, que carregam o
texto e a convenção juntos:
use ArOnline\Sdk\Legacy\Model\DateConvention; $status = $client->legacy()->status()->whatsapp($idEmail); if ($status->dateDelivery->happened()) { echo $status->dateDelivery->value; // 18/07/2026 01:01:51 } $status->dateDelivery->convention === DateConvention::MissingKey; // não veio
O valor continua texto, e não DateTimeImmutable: o gateway escreve
18/07/2026 01:01:32 sem fuso, e adivinhar um fuso produziria um instante
errado com cara de certo.
Consultas da API /v3 ($client->*)
A /v3 é a API nova, com contrato limpo e validação estrita. Hoje ela é somente de leitura.
| método | o que faz | precisa de token |
|---|---|---|
templates->list(Channel?) |
lista os modelos, com filtro por canal | sim |
templates->get(id) |
busca um modelo pelo UUID | sim |
tags->list() · tags->get(id) |
suas etiquetas | sim |
allowlist->list() |
seus destinatários permitidos | sim |
freshness->get() |
o atraso da carga de dados | sim |
version->get() |
qual versão da API está no ar | não |
Modelos
$todos = $client->templates->list(); $doWhatsApp = $client->templates->list(Channel::WhatsApp); $um = $client->templates->get('9b2f-uuid');
Channel é um enum com Email, Sms, WhatsApp, Voice e Letter. Um valor
fora da lista não passa na análise estática.
Etiquetas e lista de permitidos
$etiquetas = $client->tags->list(); $uma = $client->tags->get('12'); $permitidos = $client->allowlist->list();
São recursos pessoais: respondem o que pertence a quem está no token. Um
token de integração, que não representa uma pessoa, recebe 403 nessas rotas.
Atraso da carga
$frescor = $client->freshness->get(); if ($frescor->sourcesBehind > 0) { error_log("{$frescor->sourcesBehind} de {$frescor->sourcesTracked} atrasadas"); }
Serve para responder uma pergunta prática: quando uma consulta devolve menos do que você esperava, o problema é a API ou a carga de dados está atrasada?
Versão
$info = $client->version->get(); echo $info->version, ' ', $info->environment;
É a única chamada que funciona sem token, útil para conferir a instalação antes de ter uma credencial.
Tratamento de erros
Chamada que não lançou exceção deu certo. Você não precisa ler status HTTP nem procurar campo de erro no corpo da resposta.
A /v3 lança ApiException:
use ArOnline\Sdk\Exception\ApiException; try { $client->templates->get('nao-existe'); } catch (ApiException $error) { echo $error->errorCode; // 'not_found' echo $error->status; // 404 echo $error->requestId; // informe este número ao abrir um chamado }
| propriedade | conteúdo |
|---|---|
status |
o status HTTP (0 quando a API não foi alcançada) |
errorCode |
o código do catálogo: not_found, forbidden, rate_limited, … |
getMessage() |
a mensagem da API, em português |
requestId |
identifica a chamada nos nossos registros |
field |
o campo recusado, quando a recusa é sobre um campo |
details |
uma entrada por campo, em erro de validação |
retryAfterSeconds |
quantos segundos esperar, em 429 e 503 |
isRetryable() |
true em 429 e 503 |
Erro de rede e resposta que não é JSON também chegam como ApiException: você
trata um catch, não três.
A API legada lança LegacyApiException, com os campos do contrato antigo:
use ArOnline\Sdk\Exception\LegacyApiException; try { $client->legacy()->templates()->get('nao-existe'); } catch (LegacyApiException $error) { echo $error->status; // 404 — o código que vale echo $error->httpStatus; // 200 — o que o protocolo disse }
| propriedade | conteúdo |
|---|---|
status |
o código que vale, mesmo quando o HTTP respondeu 200 |
httpStatus |
o status que veio no protocolo (0 quando o gateway não foi alcançado) |
getMessage() |
a mensagem do gateway |
body |
o corpo da resposta, exatamente como chegou |
Os dois casos em que essa diferença aparece são contrato, não defeito. A família
de templates responde 200 com o código de verdade dentro do corpo, e o SDK
converte o 403, 404 ou 500 de dentro em exceção. E quando a validação
recusa vários campos de uma vez, o gateway manda uma lista de frases no
lugar de uma: o SDK junta as frases na mensagem e deixa a lista intacta em
body.
O SDK não repete chamadas automaticamente, porque só quem chamou sabe se a operação pode acontecer duas vezes.
Configuração do cliente
new Client( token: '…', // credencial da /v3 baseUrl: 'https://v3.ar-online.com.br', // padrão timeout: 30, // padrão, em segundos, vale para as duas legacyToken: '…', // credencial do gateway legacyBaseUrl: 'https://api.ar-online.com.br', // padrão );
Cada credencial é opcional: informe só a da superfície que você vai usar. Uma
não vaza na área da outra, e a do gateway vai crua no cabeçalho
authorization, sem Bearer, que é o oposto da /v3. Chamar a área de legado
sem legacyToken falha antes de sair da máquina, dizendo qual token falta.
As respostas viram objetos readonly — $template->name, e não
$template['name'] — com a análise estática avisando quando o campo não existe.
Campo que a API responde null continua null, nunca vira 0 nem string
vazia: um modelo sem assunto não é um modelo com assunto em branco.
Os objetos da área de legado usam os nomes de campo como a API antiga os
escreve (customID, idEmail, nome, conteudo, voz, carta). Não há
camada de tradução, para que o que você lê no SDK seja o mesmo que você vê na
documentação do gateway e nos nossos registros de suporte.
Webhooks
Em vez de consultar o status repetidamente, você pode receber uma chamada POST
a cada mudança. A configuração é feita com o suporte, que cadastra o seu endpoint
e os parâmetros de autenticação. O SDK não recebe a requisição por você, mas
traz os tipos do payload:
use ArOnline\Sdk\Legacy\Model\WebhookPayloadV1; use ArOnline\Sdk\Legacy\Model\WebhookPayloadV2; $payload = WebhookPayloadV1::fromArray(json_decode($corpo, true));
No payload v2, o campo payload é o status do canal — decidido pelo campo
channel, e não pelo formato: os canais se sobrepõem, e um payload de e-mail
satisfaz o formato da voz inteiro.
Veja https://docs.ar-online.com.br/webhooks/visao-geral para o fluxo completo, incluindo a política de retentativas.
As duas superfícies, e o caminho entre elas
A API legada é a que está em produção hoje e concentra envio, status e comprovantes. A /v3 é a API nova, para onde as funcionalidades estão sendo migradas aos poucos.
Quando uma rota ganha equivalente na /v3, o método correspondente de
$client->legacy() passa a falar com a /v3 internamente, sem mudar de
assinatura. Na prática, você migra atualizando o pacote, não reescrevendo a
sua integração. Cada troca dessas é registrada no CHANGELOG.
O equivalente de hoje: a leitura de modelos do gateway tem a /v3 em
$client->templates; envio, status e comprovantes ainda não têm.
Desenvolvimento
composer install composer run verify
verify roda o portão inteiro:
| comando | o que cobra |
|---|---|
composer run format:check |
PHP-CS-Fixer, PSR-12 com declare(strict_types=1) |
composer run lint |
PHPStan nível 9 |
composer run spell |
codespell |
composer run test:coverage |
PHPUnit contra um servidor de teste real |
composer run coverage:gate |
reprova abaixo de 95% de linhas |
composer audit |
vulnerabilidade conhecida em dependência |
| métrica | valor |
|---|---|
| Testes | 89 |
| Asserções | 254 |
| Cobertura de linhas | 99,82% |
| Análise estática | PHPStan nível 9, limpo |
| Dependências de produção | 0 |
A medição de cobertura precisa de pcov ou xdebug instalado. Sem um dos
dois, test:coverage roda os testes mas não mede nada, e o portão reprova
dizendo isso em vez de passar em silêncio. O CI usa pcov.
Os testes sobem um php -S real em uma porta livre e falam com ele por cURL. O
que o SDK precisa acertar é o comportamento na rede: qual rota embrulha a
resposta, como a recusa volta e o que acontece quando algo que não é a API
responde.
Para publicar uma versão, veja PUBLICANDO.md.
Suporte
- Dúvidas de integração e emissão de credenciais: suporte@ar-online.com.br
- Telefone: +55 (11) 4200-7766
- Defeitos neste SDK: issues do repositório
Ao abrir um chamado sobre uma chamada que falhou, informe o requestId do erro:
é com ele que localizamos a requisição nos nossos registros.
Licença
Apache License 2.0 — veja LICENSE.
© 2026 AR ONLINE TECNOLOGIA LTDA.