Ir a la documentación
PHP

Configuración

Cómo construir un cliente, todas las opciones y lo que rechaza antes de enviar una solicitud.

Opciones

clients.php
use OpenEmail\OpenEmail; $client = new OpenEmail(); OpenEmail::init(timeout: 10);OpenEmail::getClient()->me->ping(); $billing = new OpenEmail(apiKey: (string) getenv('OPENEMAIL_BILLING_API_KEY')); echo $client->mode, ' ', $billing->mode, PHP_EOL;
Punto de entradaQué te ofrece
new OpenEmail(...)Un cliente construido con los argumentos nombrados que le pasas. Todo lo que omitas se lee del entorno: la clave de OPENEMAIL_API_KEY o un token de OPENEMAIL_ACCESS_TOKEN cuando no pasas ninguna credencial, y la URL base de OPENEMAIL_BASE_URL cuando no pasas ninguna.
OpenEmail::createClient(...)El mismo cliente que new OpenEmail(...), para el código que prefiere llamar a una factoría.
OpenEmail::init(...)Construye un cliente, lo guarda como cliente compartido y lo devuelve. Acepta los mismos argumentos nombrados.
OpenEmail::getClient()El cliente compartido, desde cualquier parte del proceso. Si se llama antes de init, construye uno a partir del entorno en la primera llamada.
OpenEmail::resetClient()Descarta el cliente compartido, de modo que el siguiente getClient() construye uno nuevo, que es lo que quiere un test entre un caso y otro.

Convertir getenv() en una cadena es deliberado. Una variable sin definir se convierte en una clave vacía, que el cliente rechaza con un mensaje que nombra la variable que necesita, mientras que null recurriría en silencio a OPENEMAIL_API_KEY.

options.php
use OpenEmail\Http\CurlHttpClient;use OpenEmail\OpenEmail; $client = new OpenEmail(    apiKey: (string) getenv('OPENEMAIL_API_KEY'),    baseUrl: 'https://api.openemail.uk',    httpClient: new CurlHttpClient(),    maxRetries: 2,    timeout: 30,    userAgent: 'billing-service/1.4',    headers: ['X-Team' => 'billing'],    disableUpdateNotice: true,);
OpciónValor por defectoNotas
apiKey:OPENEMAIL_API_KEYDebe empezar por oe_live_ u oe_test_. Solo se lee del entorno cuando no pasas ni apiKey: ni accessToken:.
accessToken:OPENEMAIL_ACCESS_TOKENUn token de acceso OAuth, o un callable que devuelva uno. Consulta Tokens de acceso OAuth más abajo. Pasa una clave o un token, nunca ambos.
baseUrl:https://api.openemail.ukO OPENEMAIL_BASE_URL. Las barras finales se eliminan, y se antepone https:// a un host sin esquema, o http:// a un host de esta máquina: localhost, una dirección 127.x.x.x o ::1. Nunca se envía una credencial por http sin cifrar a ningún otro host, y 0.0.0.0 o [::] se rechaza al construir el cliente, porque son direcciones en las que escucha un servidor, no a las que enviar solicitudes.
timeout:30Segundos por intento, no por llamada, que cubren la conexión y la lectura de la respuesta entera. 0 lo desactiva. files->upload espera al menos 600 segundos salvo que pases timeout: en esa llamada.
maxRetries:2Intentos adicionales después del primero, en llamadas que se pueden repetir sin riesgo. Se configura en el cliente, no por llamada. 0 desactiva los reintentos.
httpClient:CurlHttpClientLa capa HTTP: cualquier cosa que implemente OpenEmail\Http\HttpClient, como Psr18HttpClient sobre Guzzle o Symfony HttpClient, o un doble falso en una prueba. La página de clientes HTTP trata cada uno.
headers:[]Se envían en cada solicitud.
userAgent:openemail-php/<version>Se envían en cada solicitud.
disableUpdateNotice:falseOmite la comprobación, una vez por proceso, de si hay una versión más reciente en Packagist. La comprobación solo se ejecuta en la línea de comandos cuando la salida estándar es una terminal, y OPENEMAIL_DISABLE_UPDATE_NOTICE también la desactiva.

Variables de entorno

VariableQué hace
OPENEMAIL_API_KEYLa clave que usa un cliente cuando no pasas ni apiKey: ni accessToken:.
OPENEMAIL_ACCESS_TOKENUn token de acceso OAuth, que solo se lee cuando no pasas ninguna de las dos credenciales y OPENEMAIL_API_KEY no está definida, así que una clave en el entorno tiene prioridad.
OPENEMAIL_BASE_URLLa URL base cuando no pasas ninguna. A un host sin esquema, como localhost:2222, se le añade el esquema.
OPENEMAIL_DISABLE_UPDATE_NOTICECualquier valor no vacío desactiva el aviso de actualización para todos los clientes del proceso.
HTTPS_PROXY y NO_PROXY, o https_proxy y no_proxyEl proxy a través del cual se conecta cURL, y los hosts a los que se va directamente. Consulta Proxies y TLS más abajo.

Cada variable se lee primero con getenv() y después de $_SERVER y $_ENV, así que también cuenta un valor que tu framework haya cargado de un archivo .env. Una variable definida pero vacía cuenta como no definida.

Lo que rechaza antes de enviar

Estos casos lanzan OpenEmail\Exception\InvalidArgumentException desde la línea que contenía el valor incorrecto, en lugar de aparecer como un fallo confuso en tu primer envío. El mensaje indica qué estaba mal y qué pasar en su lugar, y nunca repite una credencial.

RechazadoPor qué
Ninguna credencialNo se pasó ni apiKey: ni accessToken:, y tampoco estaba definida ninguna de las dos variables, así que no hay nada con lo que autenticarse. Se lanza al construir el cliente.
Una clave y un token a la vezCada solicitud lleva una sola credencial, así que el cliente no puede saber a cuál te referías.
Una cookie de sesión, un token de sesión o una clave de otro servicioAquí solo autentican oe_live_ y oe_test_, y la API lo confirma. La comprobación es un prefijo y nada más, así que una clave revocada sigue fallando en la red, como AuthenticationException.
Un baseUrl: que no es una URL http o https, o que contiene un nombre de usuario o una contraseñaNo se puede llegar a nada más, y una credencial va en apiKey: o accessToken:, no en la URL. Se lanza al construir el cliente.
Una credencial por http sin cifrar hacia un host que no está en esta máquinaLo lanza la llamada, antes de enviar nada. Usa una URL base https.
Un timeout: negativoPasa segundos, o 0 para no tener tiempo de espera. Se lanza al construir el cliente, o lo lanza la llamada si el tiempo de espera se pasó a una sola llamada.
Un nombre de cabecera que no es un token, o un salto de línea u otro carácter de control en el valor de una cabeceraSe comprueba en headers:, userAgent: e idempotencyKey:, porque un salto de línea iniciaría una segunda cabecera. Antes se quitan los espacios, tabuladores y saltos de línea alrededor del valor, como hace fetch, así que una clave leída de un archivo que termina en un salto de línea sigue funcionando.
Un id vacío o compuesto solo de puntos en cualquier métodoSe lanza al llamar al método. Todos los analizadores de URL eliminan un segmento de ruta formado por puntos, así que la solicitud llegaría a otro endpoint. También se rechaza un id que no sea UTF-8 válido.
Contenido de adjunto que no está en base64Una cadena siempre se lee como base64, así que unos bytes sin procesar en ella se enviarían como basura. Codifícalos con OpenEmail::toBase64(), o pasa un SplFileInfo, un stream o un stream PSR-7 y el cliente los codifica.

La clase extiende la InvalidArgumentException propia de PHP, así que el código que ya la captura sigue funcionando, e implementa OpenEmail\Exception\OpenEmailException como cualquier otra excepción que lanza el paquete. Un valor del tipo incorrecto, como un número donde va un id de tipo cadena, es un TypeError del propio PHP, porque cada método declara sus tipos.

No existe una opción testMode: ni la habrá. El esquema de la clave forma parte de la credencial en lugar de ser una pista, así que el modo es una propiedad de la clave. $client->mode lee el prefijo, live o test, y no decide nada.

Un cliente, varias claves

Construye el cliente una vez y compártelo. Un cliente nuevo por solicitud desecha su conexión abierta sin ganar nada, y ninguno de sus estados es propio de cada llamante.

Para el caso que de otro modo obligaría a un cliente por clave, como un trabajo que envía en nombre de varios espacios de trabajo, pasa apiKey: en la llamada. Sustituye la cabecera Authorization de esa solicitud y no deja nada en el cliente.

per_call_key.php
$message = [    'from' => '[email protected]',    'to' => '[email protected]',    'subject' => 'Your invoice',    'text' => 'Attached.',];$workspaceKey = (string) getenv('OPENEMAIL_API_KEY'); $client->emails->send($message); $client->emails->send($message, apiKey: $workspaceKey); $client->threads->list(folder: 'inbox', apiKey: $workspaceKey);$client->webhooks->list(apiKey: $workspaceKey);

Todos los métodos fuera de tempMail la aceptan como su último argumento nombrado, después de los filtros en una lista, y los métodos de tempMail aceptan inboxToken: en su lugar. Se comprueba antes de enviar la solicitud, con la misma regla que usa el cliente, así que una errata lanza una InvalidArgumentException sobre la apiKey pasada a esta llamada en lugar de un 401 sobre una credencial que luego tienes que ir a buscar. Una llamada reintentada conserva la clave que recibió.

$client->mode describe la clave con la que se CONSTRUYÓ el cliente y no sigue a una sustitución puntual. En cuanto un cliente sirve a varias claves no hay un único modo que informar, así que dedúcelo de la clave que pasaste. var_dump($client) muestra el modo y la URL base, nunca la clave, y cada parámetro que recibe una credencial está marcado con #[\SensitiveParameter], así que una traza de pila imprime un marcador en su lugar.

Endpoints que ningún método envuelve

$client->raw es el transporte por el que pasa cada método. $client->raw->request() llama a una ruta que ningún método envuelve todavía, aplicando la credencial, la URL base, el tiempo de espera y la política de reintentos del cliente, y devuelve el cuerpo decodificado igual que un método.

raw_request.php
$ping = $client->raw->request('/ping'); $label = $client->raw->request('/labels', method: 'POST', body: ['name' => 'Invoices']); var_dump($ping, $label);
Argumento nombradoQué hace
method:GET salvo que indiques otro: POST, PUT, PATCH o DELETE.
query:Un array de parámetros de consulta. Los valores null y vacíos se omiten, una lista se une con comas, y un DateTimeInterface se envía como un instante ISO 8601 en UTC.
body:Un array, enviado como JSON.
raw: y contentType:Bytes que se envían tal cual, como cadena, recurso de stream, SplFileInfo o stream PSR-7, con application/octet-stream salvo que indiques un tipo.
accept: y binary:Un accept: distinto de JSON devuelve el cuerpo como texto, y binary: true lo devuelve como una cadena de bytes.
idempotent: e idempotencyKey:idempotent: true adjunta un Idempotency-Key, que se genera salvo que pases el tuyo.
repeatable:Si un fallo se reintenta. Solo se reintenta un GET, salvo que pases repeatable: true.
anonymous:true no envía ninguna credencial.
apiKey:, inboxToken: y timeout:Las mismas credenciales por llamada, y un tiempo de espera en segundos solo para esta llamada.

La ruta debe empezar por una sola /, y una ruta cuya URL final saldría del origen de la URL base lanza InvalidArgumentException antes de enviar nada, así que la credencial nunca llega a otro host.

Bandejas desechables

OpenEmail::createTempMail() construye un cliente para buzones desechables que no lleva ninguna clave de API ni lee ninguna del entorno. Crea buzones de forma anónima, y cada lectura envía el token de buzón que devolvió create, o el más reciente que devolvió extend, bien por llamada como inboxToken:, bien una sola vez como OpenEmail::createTempMail(inboxToken: ...).

temp_mail.php
use OpenEmail\OpenEmail; $tempMail = OpenEmail::createTempMail(); $inbox = $tempMail->create();$page = $tempMail->listMessages($inbox['id'], inboxToken: $inbox['token']); echo count($page->items), ' ', $page->expiresAt, PHP_EOL;

OpenEmail::createTempMail() acepta baseUrl:, httpClient:, maxRetries:, timeout:, userAgent:, headers: y disableUpdateNotice: como cualquier cliente, y lee OPENEMAIL_BASE_URL cuando no pasas ninguna URL base.

Tokens de acceso OAuth

Una app que una persona conectó por OAuth, como una herramienta de línea de comandos o un agente, tiene un token de acceso en lugar de una clave de API. Pásalo como accessToken:, ya sea el propio token o un callable que lo devuelva, como un closure o un callable de primera clase. El callable se ejecuta una vez por cada llamada, y los reintentos de esa llamada reutilizan lo que devolvió, así que renueva el token dentro de él cuando esté a punto de caducar y nunca tendrás que reconstruir el cliente.

access_token.php
use OpenEmail\OpenEmail; $tokens = ['current' => 'token-from-your-oauth-flow']; $oauthClient = new OpenEmail(accessToken: fn(): string => $tokens['current']); $me = $oauthClient->me->get(); if ($me['object'] === 'oauth_token') {    echo $me['clientId'], ' ', $me['expiresAt'], PHP_EOL;}
CasoQué ocurre
apiKey: y accessToken: a la vez, o ningunoEl cliente lanza InvalidArgumentException al construirse. Si no hay ninguno, el mensaje nombra OPENEMAIL_API_KEY y OPENEMAIL_ACCESS_TOKEN.
Un valor que no es un tokenUn token tiene de 1 a 512 caracteres y no empieza por oe_, la comprobación que hace OpenEmail::isAccessToken(). Una cadena que no la supera se rechaza al construir el cliente, y un callable que devuelve una así hace que la llamada lance InvalidArgumentException antes de enviar nada.
OPENEMAIL_ACCESS_TOKENSe lee cuando no pasas ninguna de las dos credenciales y OPENEMAIL_API_KEY no está definida, así que una clave en el entorno tiene prioridad.
Un callable que lanza una excepciónLa llamada lanza esa misma excepción, sin cambios, y no se envía nada.
Un apiKey: por llamadaSustituye el token para esa única solicitud, y el invocable no se invoca.
$client->modeSiempre live con un token.
OpenEmail::createTempMail()No envía ninguna credencial, contenga lo que contenga el entorno.
me->get() y me->ping()Para un token, get responde con object igual a oauth_token, id y roleId a null, el clientId de la app conectada y expiresAt, el momento en que caduca la aprobación que la persona dio a la app. ping responde con kind igual a oauth, keyId a null y el clientId. Comprueba object o kind antes de leer id o keyId.

Un token actúa en nombre de una persona y lee su correo como ella puede, así que guárdalo en un servidor igual que una clave.

Códigos de verificación

Antes de un cambio delicado, como eliminar un dominio o cambiar un webhook, la API pide a un token de acceso el código de verificación que la app web le pediría a la persona. La llamada lanza una PermissionException, un 403 cuyo isStepUpRequired() es true, y no se cambió nada. Pide un código, verifica el que te dé la persona y vuelve a hacer la llamada. A una clave de API nunca se le pide.

step_up.php
use OpenEmail\Exception\ApiException; $domainId = 'b3e1f0a4-6c2d-4e8a-9f17-2d5c8a0b4e6f'; try {    $client->domains->delete($domainId);} catch (ApiException $error) {    if (!$error->isStepUpRequired()) {        throw $error;    }     $challenge = $client->security->beginStepUp();     if ($challenge['method'] === 'email') {        echo 'Enter the code we emailed to ', $challenge['sentTo'], PHP_EOL;    } else {        echo 'Enter the code from your authenticator app, or a backup code', PHP_EOL;    }     $client->security->verifyStepUp(['code' => trim((string) fgets(STDIN))]);    $client->domains->delete($domainId);}
MétodoQué hace
security->stepUpStatus()Si la app está verificada ahora mismo (elevated, elevatedUntil), cómo se comprueba el siguiente código (method, email o totp) y minutes, la duración de la ventana. No envía nada ni informa de una pausa.
security->beginStepUp()Abre un desafío. Con email sale un código de seis dígitos hacia la dirección con la que la persona inicia sesión, y sentTo la muestra enmascarada. Con totp la persona lee uno en su aplicación de autenticación o usa un código de recuperación. Un desafío que sigue abierto y aún tiene intentos se reutiliza salvo que pases ['resend' => true], y uno bloqueado o caducado se sustituye con una llamada simple. Cada app puede abrir 5 por hora y 20 en 24 horas para cada persona, y el siguiente lanza un 429 step_up_throttled.
security->verifyStepUp(['code' => ...])Comprueba el código y desbloquea los cambios delicados para esta app durante 60 minutos, hasta elevatedUntil, por REST y a través de las herramientas MCP que hacen los mismos cambios. Tras 10 códigos incorrectos en 24 horas de esta app, o 20 de todas las apps de la persona juntas, esta llamada y beginStepUp lanzan un 429 step_up_locked con un mensaje que dice cuándo se reanuda la verificación.

El cliente nunca pide un código ni repite la llamada por su cuenta, y ninguno de los tres métodos se reintenta automáticamente, porque un reintento tras una respuesta perdida podría enviar un segundo correo o gastar un segundo intento. No necesitan ningún ámbito, y una clave de API que llame a uno recibe un 400 step_up_not_applicable. OpenEmail\Constants\StepUpErrorCodes nombra todas las formas en que puede fallar una verificación, y la página de errores de la API dice qué hacer en cada caso.

El aviso de actualización

Cuando hay una versión más reciente del paquete en Packagist, el cliente lo dice una vez por proceso, en la salida de error estándar, con una línea como ℹ openemail/sdk 0.0.2 is available, you are on 0.0.1. seguida de la página del paquete. La comprobación solo se ejecuta en la línea de comandos, cuando la salida estándar es una terminal, y nunca bajo un servidor web. Empieza al construir el primer cliente y corre junto a tus solicitudes, y al final del script espera lo que quede de un margen de dos segundos. Si no se puede llegar a Packagist, se ignora.

La comprobación hace su propia solicitud con cURL, fuera del httpClient: del cliente, así que un cliente HTTP falso en una prueba nunca la ve. Pasa disableUpdateNotice: true o define OPENEMAIL_DISABLE_UPDATE_NOTICE para desactivarla.

Proxies y TLS

El CurlHttpClient por defecto deja los proxies a cURL, que lee https_proxy o HTTPS_PROXY para el proxy y no_proxy o NO_PROXY para los hosts a los que se va directamente. Pasa proxy: para indicar uno en el código. Un nombre de usuario y una contraseña en la URL del proxy se envían al proxy.

curl_options.php
use OpenEmail\Http\CurlHttpClient;use OpenEmail\OpenEmail; $client = new OpenEmail(httpClient: new CurlHttpClient(    caBundle: '/etc/ssl/certs/corporate-ca.pem',    proxy: 'http://proxy.internal:3128',    curlOptions: [CURLOPT_IPRESOLVE => CURL_IPRESOLVE_V4],));

Las conexiones usan TLS 1.2 o posterior y comprueban el certificado y el nombre de host del servidor, y nunca se siguen las redirecciones. caBundle: indica las autoridades de certificación de confianza, para un proxy que inspecciona TLS. curlOptions: define cualquier otra opción de cURL, pero los ajustes que necesita una solicitud siempre prevalecen: la URL y su puerto, el método, las cabeceras, el cuerpo y las redirecciones desactivadas. CURLOPT_REQUEST_TARGET se rechaza, y una opción que cURL no acepta lanza una InvalidArgumentException que la nombra.