---
title: "Helpers and constants"
description: "What else the package defines besides the client’s methods."
url: "https://openemail.uk/docs/php/reference/helpers"
area: "PHP"
category: "Reference"
---

# Helpers and constants

What else the package defines besides the client’s methods.

## Static methods

| Method | What it is |
| --- | --- |
| `OpenEmail::init()`, `OpenEmail::getClient()` | Configure the shared client once, then reach it from anywhere. `getClient()` builds one from the environment if `init` never ran. |
| `OpenEmail::resetClient()` | Drops the shared client, so the next `getClient()` builds a fresh one, which is what a test wants between cases. |
| `new OpenEmail()`, `OpenEmail::createClient()` | A separate client. Both read the environment for anything you leave out. |
| `OpenEmail::createTempMail()` | A disposable-inbox client that carries no API key. |
| `OpenEmail::verifyWebhookSignature()` | Checks a delivery’s signature in constant time, with a five minute replay window that `toleranceSeconds:` changes. Returns the decoded event, and throws `WebhookSignatureException` on any failure. |
| `OpenEmail::toBase64()` | Base64 for attachment bytes, from a string, a stream resource, an `SplFileInfo` or a PSR-7 stream. |
| `OpenEmail::isApiKey()` | Whether a value has the `oe_live_` or `oe_test_` shape. A shape check, not proof the key still works. |
| `OpenEmail::isAccessToken()` | Whether a value has the shape of an OAuth access token: 1 to 512 characters, not beginning `oe_`. |
| `OpenEmail::isSealed()` | Whether a message’s body is ciphertext. It is false for the two signed formats, whose bodies arrived in the clear. |
| `OpenEmail::resolveLanguage()`, `OpenEmail::languageByCode()`, `OpenEmail::isRtlLanguage()` | The lookups a language picker needs, over the bundled `Languages::ALL` table. |
| `$client->close()` | Releases the client’s cURL handle and the connection behind it. A client that is no longer referenced does the same when PHP frees it. |

## Constants

Every value set the TypeScript SDK exports is a final class in `OpenEmail\Constants`, with one constant per member under the same names, so `WebhookEvents::EMAIL_DELIVERED` is `email.delivered`. `values()` returns the whole set, which is also how to check a value that came from outside.

**constants.php**

```
use OpenEmail\Constants\ApiScopes;
use OpenEmail\Constants\PageLimits;
use OpenEmail\Constants\WebhookEvents;

$events = WebhookEvents::values();

$scopes = [ApiScopes::EMAILS_SEND, ApiScopes::THREADS_READ];

$known = in_array('email.delivered', $events, true);

echo count($events), ' ', implode(',', $scopes), ' ', PageLimits::MAX_LIMIT, ' ', $known ? 'known' : 'unknown', PHP_EOL;
```

| Constant | What it holds |
| --- | --- |
| `OpenEmail::VERSION` | The package version. |
| `ApiScopes` | The scope vocabulary, for a key-creation screen. |
| `WebhookEvents`, `WebhookSignatureHeaders` | The events an endpoint can subscribe to, and the names of the headers a delivery carries. |
| `ErrorTypes` | The error vocabulary that `ApiException::$type` takes. |
| `PageLimits` | The largest and the default `limit:` on most paged lists, `MAX_LIMIT` and `DEFAULT_LIMIT`: 100 and 25. A few lists take more, and each method’s reference says so. |
| `RuleFields`, `RuleOperators`, `RuleActions` | The vocabulary a rule’s conditions and actions are built from. |
| `MessageEncryptionFormats` | The five envelopes ingest can name. Three of them are sealed. |
| `CredentialKinds`, `StepUpMethods`, `StepUpErrorCodes` | Which credential `me->get` and `me->ping` describe, how a verification code is checked, and the codes a verification can fail with. |
| `ThreadSorts`, `PeopleSorts`, `FileSorts` and the other `*Sorts` | The orders a list can be sorted in. |
| `FormStatuses`, `BroadcastStatuses`, `SuppressionReasons` and the other sets | The values a field of a resource can take. Each set is named after what it holds. |
| `Languages::ALL` | Every language a translated send or preview accepts, with its code, its names and its direction. |

## Objects

A response is the decoded JSON as an associative array. The package builds an object of its own only where it shapes the answer, and each lives in `OpenEmail\Result`, is immutable, and is `IteratorAggregate` and `Countable` over its rows.

| Class | What it carries |
| --- | --- |
| `Page` | `items`, `hasMore` and `nextCursor`, from every paged `list`. |
| `PeoplePage` | The same plus `seen`, from `contacts->listPeople`. |
| `TempMessagesPage` | The same plus `expiresAt`, from `tempMail->listMessages`. |
| `AddressBookPage`, `AddressBook` | `unrestricted`, `addresses` and `domains`, from `addresses->list` (with `hasMore` and `nextCursor`) and `addresses->listAll`. |
| `BatchResult` | `items`, `sent` and `failed`, from `emails->sendBatch`. |
| `TemplateSends` | `items`, `total`, `page` and `pageSize`, from `templates->listSends`. |
| `OpenEmail\Http\HttpRequest`, `OpenEmail\Http\HttpResponse` | What an `httpClient:` receives and returns. `var_dump()`, `print_r()` and `json_encode()` show a request’s `Authorization` header as `[redacted]`, while `var_export()` and Symfony’s `dump()` show it as it is. |

> Every exception the package throws implements `OpenEmail\Exception\OpenEmailException`: `ApiException` and its subclasses, `NetworkException`, `WebhookSignatureException` and `InvalidArgumentException`, which is thrown for a mistake in the call itself rather than something the API said.

## An endpoint this does not wrap yet

A package release should never be what stands between you and an endpoint that already works. `$client->raw->request()` takes a path and named arguments and returns the decoded body, with the client’s credential, base URL, timeout and retry policy applied.

**escape_hatch.php**

```
$result = $client->raw->request(
    '/labels',
    method: 'POST',
    query: ['dryRun' => true],
    body: ['name' => 'Invoices'],
    repeatable: true,
);

var_dump($result);
```

> A GET is retried like any other read. Any other method is sent once unless you pass `repeatable: true`, which is your assertion that it may be sent twice. `query:` skips values that are null or empty, and `apiKey:` works as it does on every other method.

## What it deliberately does not do

- It validates no request body. The server’s schema is the only copy of the rules, and a second copy here would eventually refuse an address a newer server accepts, in a version somebody pinned two years ago.
- It has no runtime dependencies beyond the curl and json extensions. A PSR-18 client is an option, never a requirement.
- It reshapes a response in one way only: a collection’s `data` array is lifted out of its envelope into one of the objects above. Every other response comes back as the API sent it, with the API’s camelCase keys.

> The package’s parity check keeps this honest. It fails the build when a TypeScript method has no PHP twin, takes different arguments, or sends a different request.
