{"id":20504898,"url":"https://github.com/oxidizing/letters","last_synced_at":"2025-04-13T20:51:30.748Z","repository":{"id":47060337,"uuid":"276989811","full_name":"oxidizing/letters","owner":"oxidizing","description":"Letters is an OCaml library for creating and sending emails over SMTP using LWT","archived":false,"fork":false,"pushed_at":"2025-01-14T18:17:42.000Z","size":138,"stargazers_count":54,"open_issues_count":12,"forks_count":6,"subscribers_count":6,"default_branch":"master","last_synced_at":"2025-03-27T11:13:52.798Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"OCaml","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/oxidizing.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGES.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2020-07-03T21:25:32.000Z","updated_at":"2025-02-24T01:34:35.000Z","dependencies_parsed_at":"2024-02-29T14:53:14.986Z","dependency_job_id":null,"html_url":"https://github.com/oxidizing/letters","commit_stats":null,"previous_names":[],"tags_count":8,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/oxidizing%2Fletters","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/oxidizing%2Fletters/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/oxidizing%2Fletters/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/oxidizing%2Fletters/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/oxidizing","download_url":"https://codeload.github.com/oxidizing/letters/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248782278,"owners_count":21160716,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-11-15T19:41:05.947Z","updated_at":"2025-04-13T20:51:30.728Z","avatar_url":"https://github.com/oxidizing.png","language":"OCaml","funding_links":[],"categories":[],"sub_categories":[],"readme":"# \u0026#9993; Letters \u0026middot; ![GitHub](https://img.shields.io/github/license/oxidizing/letters)\n\nLetters is a library for creating and sending emails over SMTP using [Lwt](https://github.com/ocsigen/lwt).\n\n## Table of Contents\n\n* [Use](#use)\n  * [Configuration](#configuration)\n  * [Building emails](#building-emails)\n  * [Sending emails](#building-emails)\n  * [Examples](#examples)\n* [Development](#development)\n  * [Setup](#setup)\n  * [Build](#build)\n  * [Tests](#tests)\n    * [Unit tests](#unit-tests)\n    * [Service tests](#service-tests)\n* [Credits](#credits)\n* [License](#license)\n\n## Use\n\nPurpose of the library is to make it easier to send emails when building systems using OCaml. Currently the API consists of three parts:\n\n1. configuration\n2. building email messages\n3. sending email messages\n\nWhole API is in `lib/letters.mli` that contains also some additional documentation.\n\nKeep in mind that this library is in its early days and the API is changing with every release. Also this is tested only on Linux based systems and testing is pretty weak and manual. Though the library has been used successfully.\n\n### Configuration\n\nMost simple use case would look something like:\n\n``` ocaml\nlet conf = Config.create ~username:\"myuser\" ~password:\"mypasswd\" ~hostname:\"smtp.ethereal.email\" ~with_starttls:true ()\n```\n\nThis will use port `587`, uses STARTTLS for encryption and tries automatically find CA certificates for verifying server connection.\n\nPort `587` is default when using STARTTLS. If you set `~with_starttls:false`, then the default port will be `465`.\n\nThis library does **not** support SMTP connections without TLS encryption. For TLS encryption, this library uses [ocaml-tls](https://opam.ocaml.org/packages/tls/).\n\nIf you want to change the server port you can do it with `Config.set_port` (passing `None` causes default port to be used):\n\n``` ocaml\nlet conf = Config.create ~username:\"myuser\" ~password:\"mypasswd\" ~hostname:\"smtp.ethereal.email\" ~with_starttls:true ()\n|\u003e Config.set_port (Some 2525)\n```\n\nCA certificate auto-detection is done once initially when you call `Letters.send`. If the CA certificate auto-detection does not work for you (whether you plan on moving the CA certificate after calling `Letters.send` initially or whether the detection simply fails on your system), you can define path to a certificate bundle or to a single PEM encoded certificate, or you can define path to a folder containing multiple PEM encoded certificate files.\n\nTo use a CA certificate bundle (each included certificate needs to be PEM encoded):\n\n``` ocaml\nlet conf = Config.create ~username:\"myuser\" ~password:\"mypasswd\" ~hostname:\"smtp.ethereal.email\" ~with_starttls:true ()\n|\u003e Config.set_ca_cert \"/etc/ssl/certs/ca-certificates.crt\"\n```\n\nTo use a single PEM encoded CA certificate:\n\n``` ocaml\nlet conf = Config.create ~username:\"myuser\" ~password:\"mypasswd\" ~hostname:\"smtp.ethereal.email\" ~with_starttls:true ()\n|\u003e Config.set_ca_cert \"/etc/ssl/certs/DST_Root_CA_X3.pem\"\n```\n\nTo use all PEM encoded certificate files from a folder:\n\n``` ocaml\nlet conf = Config.create ~username:\"myuser\" ~password:\"mypasswd\" ~hostname:\"smtp.ethereal.email\" ~with_starttls:true ()\n|\u003e Config.set_ca_path \"/etc/ssl/certs/\"\n```\n\n### Building emails\n\nBuilding an email is separated into its own step so that you can use [mrmime](https://opam.ocaml.org/packages/mrmime/) to generate more complex emails when this simplified API does not work for you.\n\nTo use our provided API, you can build three kinds of emails:\n\n1. `Plain`, plain text\n2. `Html`, HTML only\n3. `Mixed`, multipart/alternative containing both: plain text and HTML segments\n\nIf you're not sure, either use `Plain` or `Mixed`.\n\nExample of building a plain text email:\n\n``` ocaml\nlet sender = \"harry@example.com\" in\nlet recipients =\n  [\n    To \"larry@example.com\";\n    Cc \"bill@example.com\";\n    Bcc \"dave@example.com\";\n  ]\nin\nlet subject = \"Plain text only test email\" in\nlet body =\n  Plain\n    {|\nHi there,\n\nThis is a test email from https://github.com/oxidizing/letters\n\nRegards,\nThe Letters team\n|}\n  in\n  let mail = create_email ~from:sender ~recipients ~subject ~body () in\n```\n\nExample of building an HTML only email:\n\n``` ocaml\nlet sender = \"harry@example.com\" in\nlet recipients =\n  [\n    To \"larry@example.com\";\n    Cc \"bill@example.com\";\n    Bcc \"dave@example.com\";\n  ]\nin\nlet subject = \"HTML only test email\" in\nlet body =\n  Html\n    {|\n\u003cp\u003eHi there,\u003c/p\u003e\n\u003cp\u003e\n    This is a test email from\n    \u003ca href=\"https://github.com/oxidizing/letters\"\u003eletters\u003c/a\u003e\n\u003cp\u003e\nRegards,\u003cbr\u003e\nThe Letters team\n\u003c/p\u003e\n|}\nin\nlet mail = create_email ~from:sender ~recipients ~subject ~body () in\n```\n\nExample of building an email with plain text and HTML segments:\n\n``` ocaml\nlet sender = \"harry@example.com\" in\nlet recipients =\n  [\n    To \"larry@example.com\";\n    Cc \"bill@example.com\";\n    Bcc \"dave@example.com\";\n  ]\nin\nlet subject = \"Mixed plain text / HTML test email\" in\nlet text =\n  {|\nHi there,\n\nThis is a test email from https://github.com/oxidizing/letters\n\nRegards,\nThe Letters team\n|}\nin\nlet html =\n  {|\n\u003cp\u003eHi there,\u003c/p\u003e\n\u003cp\u003e\n    This is a test email from\n    \u003ca href=\"https://github.com/oxidizing/letters\"\u003eletters\u003c/a\u003e\n\u003cp\u003e\nRegards,\u003cbr\u003e\nThe Letters team\n|}\nin\nlet mail = create_email ~from:sender ~recipients ~subject ~body:(Mixed (text, html, None)) () in\n```\n\n`Letters.create_email` returns `result` so you need to map it accordingly:\n\n``` ocaml\nlet mail = create_email ~from:sender ~recipients ~subject ~body:(Mixed (text, html, None)) () in\nmatch mail with\n| Ok message -\u003e do_something message\n| Error reason -\u003e handle_error reason\n```\n\n### Sending emails\n\nSending is single API call `Letters.send` that looks like following (when using `config`, `sender`, `recipients` and `message` from previous examples):\n\n``` ocaml\nsend ~config ~sender ~recipients ~message\n```\n\nReturn type is `Lwt.t` so you need to run it with appropriate `Lwt` routines.\n\n### Examples\n\nSee `service-test/test.ml` for complete examples that are using *ethereal.email* service to test sending emails.\n\n## Development\n\n### Setup\n\n``` shell\nopam switch create . ocaml-base-compiler.4.08.1\neval $(opam env)\nopam install --deps-only -y . --with-test\n```\n\n### Build\n\n``` shell\ndune build\n```\n\n### Tests\n\n#### Unit tests\n\nRun with default `test` target of `dune`:\n\n``` shell\ndune build @runtest\n```\n\nThese tests are still somewhat far from good and you need to validate all results manually by checking the test output logs.\n\n#### Service tests\n\nBecause these tests are somewhat slow and fragile, you need to run them manually. Execution of these tests depends on test accounts on *ethereal.email* and *mailtrap.io*. Before execution, you need to create configuration files with authentication credentials for each service. You can generate these configuration files by using the shell command snippets given below, but for those you need to have `jq` application installed. If you don't have it or you don't want to install it, you can also create `ethereal_account.json` and `mailtrap_account.json` files manually. Both files have the following format:\n\n``` json-with-comments\n{\n  \"host\": \"smtp.ethereal.email or smtp.mailtrap.io\",\n  \"port\": 587,\n  \"username\": \"username for SMTP authentication\",\n  \"password\": \"password for SMTP authentication\",\n  \"secure\": false // we will always use encryption, but `false` causes use of STARTTLS\n}\n```\n\nTo create temporary *ethereal.email* account and store the account details, you can execute the following one-liner:\n\n``` shell\ncurl -s -d '{ \"requestor\": \"letters\", \"version\": \"dev\" }' \"https://api.nodemailer.com/user\" -X POST -H \"Content-Type: application/json\" | jq '{ hostname: .smtp.host, port: .smtp.port, secure: false, username: .user, password: .pass, }'\u003e ethereal_account.json\n```\n\nFor *mailtrap.io*, you need to create a personal account first and get the API key (for v1 API):\n\n* [signup](https://mailtrap.io/register/signup?ref=header)\n* click on your name top right and select \"My Profile\", copy the \"Api Token\"\n\nThe configuration file you can create with following steps:\n\n* create environment variable containing your API token: `export MAILTRAP_API_TOKEN=\u003cAPI token\u003e`\n* run the following one-liner in terminal to create the configuration file:\n\n``` shell\ncurl -s -H \"Api-Token: ${MAILTRAP_API_TOKEN}\" \"https://mailtrap.io/api/v1/inboxes\" | jq '.[0] | { hostname: .domain, port: .smtp_ports[2], secure: false, username: .username, password: .password }' \u003e mailtrap_account.json\n```\n\nNow you are ready to execute these tests. You can run them with the following command:\n\n``` shell\ndune build @runtest-all\n```\n\nFinally review that all emails are correctly received in *ethreal.email*:\n\n* login to \u003chttps://ethereal.email/login\u003e using credentials from the `ethereal_account.json`\n* check the content of new messages: \u003chttps://ethereal.email/messages\u003e\n\nAlso check that you can find all emails in the inbox in *mailtrap.io*:\n\n* login to [mailtrap.io](https://mailtrap.io/signin) using your personal credentials\n* select the first inbox (unless you use another one), from [inboxes](https://mailtrap.io/inboxes)\n* check the content of new messages\n\n## Credits\n\nThis project is build on [colombe](https://github.com/mirage/colombe \"colombe project\") and [mrmime](https://github.com/mirage/mrmime \"mrmime project\") libraries and use [facteur](https://github.com/dinosaure/facteur \"facteur email sending tool\") as starting point.\n\n## License\n\nCopyright (c) 2020 Miko Nieminen\n\nDistributed under the MIT License.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Foxidizing%2Fletters","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Foxidizing%2Fletters","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Foxidizing%2Fletters/lists"}