Файлы
`files->list`, `listAll`, `iterate`, `get`, `download`, `listLinks`, `listAllLinks`, `iterateLinks`, `createLink`, `revokeLink`, `revokeAllLinks`, `stats`, `upload`, `delete` и `deleteMany`.
Все методы
use OpenEmail\Constants\FileKinds;use OpenEmail\Constants\FileSorts; $largest = $client->files->list(kind: FileKinds::PDF, sort: FileSorts::LARGEST, limit: 1); foreach ($largest as $file) { foreach ($client->files->listAllLinks($file['id']) as $link) { echo $link['url'], ' ', $link['downloads'], PHP_EOL; } file_put_contents(basename($file['filename']), $client->files->download($file['id']));} $stats = $client->files->stats();echo $stats['totals']['files'], ' ', $stats['uploaded']['bytes'], PHP_EOL;Все файлы, которые хранит почтовый ящик: отправленные и полученные вложения и загруженные в него файлы. Это страница «Файлы» в приложении. download возвращает байты как строку, которую file_put_contents сохраняет без изменений. Ссылки являются ссылками для скачивания, с которыми файл уходил, вместе с тем, сколько раз каждая была запрошена, а stats соответствует вкладке «Аналитика».
list, listAll и iterate принимают q:, kind: и sort:, а также direction: (inbound, outbound или uploaded), address: (файлы одного адреса, сравнение без учёта регистра) и since: и until: в виде DateTimeInterface или строки ISO 8601. since: включает свой момент, а until: останавливается перед ним. DateTimeInterface отправляется в UTC, а строка с голой датой, например 2026-09-01, читается как полночь UTC.
q: ищет по имени файла и его типу. kind: принимает image, pdf, audio, video или text, а sort: принимает newest (по умолчанию), oldest, largest или name. OpenEmail\Constants\FileKinds, FileSorts и FileDirections перечисляют варианты. list возвращает одну OpenEmail\Result\Page, а listAll возвращает все файлы одним массивом. iterate возвращает Generator, который выдаёт файлы по одному и запрашивает следующую страницу, только когда она нужна циклу.
Для чтения нужна files:read, а для загрузки, удаления и публикации или отзыва ссылок нужна files:write, которая включает files:read. Ключ, созданный до появления у файлов собственных областей, получил соответствующие. Ключ без нужной области отклоняется с 403 insufficient_scope, выбрасываемым как PermissionException, у которого isScopeMissing() равно true. Ключ, ограниченный определёнными адресами или доменами, видит только файлы, пришедшие на них, поэтому не видит файлов, загруженных для всего рабочего пространства.
download держит весь файл в памяти, поэтому очень большие файлы скачивайте по одному. Пропускайте имя через basename() перед записью, как в примере, потому что его задал тот, кто прислал файл.
Публичные ссылки
use OpenEmail\Constants\FileDirections; $uploads = $client->files->listAll(direction: FileDirections::UPLOADED, since: new \DateTimeImmutable('2026-09-01T00:00:00Z'));$file = $uploads[0] ?? null; if ($file !== null) { $link = $client->files->createLink($file['id'], domain: 'acme.com'); echo $link['url'], PHP_EOL; $client->files->revokeLink($file['id'], $link['id']);}createLink публикует файл по публичной ссылке для скачивания, которая открывается без входа, и возвращает ссылку. Ссылка размещается на хосте файлов domain:, если у этого домена он есть, например files.acme.com, иначе на хосте файлов адреса, которому принадлежит файл, иначе на адресе API. Файл, который пришёл или ушёл с сообщением, сначала копируется в публичное хранилище, а программа или скрипт отклоняются с 422 file_unshareable, выбрасываемым как ValidationException.
Каждый createLink создаёт новую ссылку, поэтому он никогда не повторяется после потерянного ответа: сначала поищите ссылку через listLinks. revokeLink навсегда отключает ссылку, в том числе в уже отправленных письмах, и возвращает её с заданным revokedAt. Повторный отзыв безопасен, поэтому клиент повторяет его как чтение. Неизвестная ссылка даёт 404, выбрасываемый как NotFoundException. revokeAllLinks отключает сразу все ссылки одного файла и сообщает их число в revoked.
Загрузка и удаление
$report = "month,sent\n2026-09,1200\n";$upload = $client->files->upload($report, filename: 'report.csv', contentType: 'text/csv'); if ($upload['deletable']) { $client->files->delete($upload['id']);} $logo = $client->files->upload(new \SplFileInfo('logo.svg'));echo $logo['filename'], ' ', $logo['mimeType'], PHP_EOL; $result = $client->files->deleteMany(['file_0c4e7a91d2b84f63a5e19b7d', 'file_6bb640f5b99e47deb758f1f5']); foreach ($result['kept'] as $kept) { echo $kept['filename'], ' ', $kept['reason'], PHP_EOL;}upload сохраняет байты как есть, до 100 МБ, под именем из filename: и возвращает файл. Отправка прикрепляет его как ['fileId' => $upload['id']] в attachments вызова emails->send. Передайте contentType: или данные, которые несут собственный тип, а всё остальное сохраняется как application/octet-stream. Тип никогда не угадывается по filename:, поэтому строке байтов нужен contentType:, чтобы сохраниться как что-то другое. Вызов никогда не повторяется, потому что вторая попытка сохранила бы вторую копию.
Данные передаются как строка байтов, SplFileInfo, поток из fopen() либо поток или загруженный файл PSR-7. SplFileInfo или потоку, открытому на файле, не нужен ни один из этих аргументов: имя берётся у самого файла, а тип выводится из расширения, если пакет его знает, например .pdf или .png. Загрузка Laravel или Symfony, а также загруженный файл PSR-7 приносят имя и тип, которые прислал браузер. Строка не несёт имени, поэтому строка без filename: выбрасывает InvalidArgumentException ещё до отправки.
Загрузка может длиться 600 секунд или timeout клиента, если он больше, а timeout: задаёт другой предел для одного вызова. Клиент, собранный с timeout: 0, ждёт столько, сколько длится загрузка.
Удалить можно только загрузку, от которой ничего не зависит, и об этом заранее говорят deletable и usage каждого файла. delete отклоняет любой другой файл с 409 file_in_use, выбрасываемым как ConflictException. deleteMany принимает до 100 идентификаторов, удаляет что может, а остальные сообщает в kept, каждый с причиной, и в missing. Ни один из них не повторяется. 404 на втором delete после потерянного ответа означает, что первый сработал, а второй deleteMany сообщает такие файлы в missing.