Files
`files.list`, `list_all`, `iterate`, `get`, `download`, `list_links`, `list_all_links`, `iterate_links`, `create_link`, `revoke_link`, `stats`, `upload`, `delete` and `delete_many`.
Every method
page = client.files.list(kind: "pdf", sort: "largest")file = page.items.first links = client.files.list_all_links(file[:id])p links.map { |link| [link[:url], link[:downloads]] } stats = client.files.statsputs stats.dig(:totals, :files), stats.dig(:uploaded, :bytes) File.binwrite(file[:filename], client.files.download(file[:id]))Every file the mailbox holds, attachments sent and received and the files uploaded to it: the Files page of the app. download returns the bytes as a binary String, which File.binwrite saves unchanged. The links are the download links a file went out as, with how often each was fetched, and stats is the Analytics tab.
list, list_all and iterate take q:, kind: and sort:, and also direction: (inbound, outbound or uploaded), address:, the files of one address compared without regard to case, and since: and until:, a Time, a DateTime or an ISO 8601 String. since: includes its moment and until: stops before it. A Ruby Date is sent as a bare date, which this list reads as midnight UTC.
q: searches the file name and its type. kind: is image, pdf, audio, video or text, and sort: is newest, the default, oldest, largest or name. OpenEmail::FILE_KINDS, OpenEmail::FILE_SORTS and OpenEmail::FILE_DIRECTIONS name the choices. list returns one OpenEmail::Page, and list_all returns every file in one Array. iterate yields each file to a block and fetches the next page only when the loop needs it. Without a block it returns an Enumerator.
Reading needs files:read, and uploading, deleting and publishing or revoking links need files:write, which includes files:read. A key made before files had scopes of their own was given the matching ones. A key without the scope is refused with a 403 insufficient_scope, and scope_missing? on the error is true. A key limited to particular addresses or domains sees only the files that arrived at them, so it does not see files uploaded for the whole workspace.
download holds the whole file in memory, so fetch very large files one at a time.
Public links
uploads = client.files.list_all(direction: "uploaded", since: Time.utc(2026, 9, 1))file = uploads.first link = client.files.create_link(file[:id], domain: "acme.com")puts link[:url] client.files.revoke_link(file[:id], link[:id])create_link publishes a file at a public download link that opens with no sign-in, and returns the link. The link lives on the files host of domain: when that domain has one, such as files.acme.com, else on the files host of the address the file belongs to, else on the API address. A file that came in or went out on a message is copied to public storage first, and a program or a script is refused with a 422 file_unshareable, raised as OpenEmail::ValidationError.
Each create_link makes a new link, so it is never retried after a lost answer: look for the link with list_links first. revoke_link stops a link for good, including in mail that already went out, and returns it with revokedAt set. Revoking one twice is safe, so the gem retries it like a read. An unknown link is a 404, raised as OpenEmail::NotFoundError.
Upload and delete
upload = client.files.upload(File.binread("report.pdf"), filename: "report.pdf", content_type: "application/pdf")client.files.delete(upload[:id]) if upload[:deletable] invoice = client.files.upload(Pathname("invoice.pdf"))puts invoice[:filename], invoice[:mimeType] result = client.files.delete_many(["file_0c4e7a91d2b84f63a5e19b7d", "file_6bb640f5b99e47deb758f1f5"])result[:kept].each { |kept| puts "#{kept[:filename]} #{kept[:reason]}" }upload stores the bytes as they are, up to 100 MB, under the name in filename:, and returns the file. A send attaches it as {fileId: upload[:id]} in the attachments of emails.send. Pass content_type:, or bytes that carry their own type, and anything else is stored as application/octet-stream. It is never retried, because a second attempt would store a second copy.
The bytes are a binary String, an IO or a Pathname. A Pathname or a File needs neither keyword: the name is the file’s own, and the type comes from its extension when the gem knows it, such as .pdf or .png. A Rails upload brings its own original_filename and content_type. Plain bytes carry no name, so a String or a StringIO without filename: raises ArgumentError before anything is sent.
An upload may run for 600 seconds, or for the client’s timeout when that is longer, and timeout: sets another limit for one call. A client built with timeout: 0 waits as long as an upload takes.
Only an upload that nothing depends on can be deleted, and deletable and usage on each file say so ahead of time. delete refuses any other file with a 409 file_in_use, raised as OpenEmail::ConflictError. delete_many takes up to 100 ids, deletes what it can, and reports the rest in kept, each with its reason, and in missing. Neither is retried. A 404 on a second delete after a lost answer means the first one worked, and a second delete_many reports those files in missing.