# Mail on the wire — Mime.Parse, Mime.Build, Imap.Fetch, Smtp.Send

> The four calls a mail kit is built from. `Mime.Parse` reads a whole RFC 5322 message — headers, text and HTML bodies, attachments, and a bounce's failed Message-IDs — into JSON in the shape of `Osysharp.Mail`'s `MailMessage`; `Mime.Build` writes that shape back as bytes. `Imap.Fetch` and `Smtp.Send` read and send through any mail host over the platform's guarded transport. An app reaches them through `Osysharp.Mail` and its mailbox kits, rarely directly.

<!-- id: stdlib-mail-wire · area: stdlib · stability: preview · html: https://osysharp.com/reference/stdlib/mail-wire/ -->

## Summary        {#summary}
A mail message is BYTES — RFC 5322 text with MIME parts inside. These calls turn those bytes into data an app can
read, data back into bytes a provider sends, and move them to and from a mail host:

```osy syntax
MailMessage mail = JsonSerializer.Deserialize<MailMessage>(Mime.Parse(raw));   // what Osysharp.Mail's ParseMail does
byte[] raw = Mime.Build(JsonSerializer.Serialize(mail));                         // …and RawOf
```

Most apps never write them: `Osysharp.Mail` wraps the first two as `ParseMail` / `RawOf`, and
`Osysharp.Mail.Imap`'s `ImapMailbox` is built on the other two. They are here for the kit you write next.

## Signature      {#signature}
```osy syntax
string Mime.Parse(byte[] raw)    // → JSON: From, To, Cc, Bcc, ReplyTo, Subject, Date, MessageId, InReplyTo,
                                 //   References, Text, Html, Headers, Attachments[{FileName, Content (base64),
                                 //   ContentType, ContentId}], FailedMessageIds (a bounce report only)
byte[] Mime.Build(string json)   // the same shape (any letter case) → RFC 5322 bytes

// `use Osysharp.Http;` — outbound network, so the same opt-in as Http.*
string Imap.Fetch(string host, int port, string username, string credential, bool oauth,
                  string folder, string cursor, int max)
string Smtp.Send(string host, int port, string username, string credential, bool oauth, byte[] message)
```

## Description    {#description}

### Parse reads everything a mail carries   {#parse}
`Mime.Parse` answers the message in `MailMessage`'s own shape, so `JsonSerializer.Deserialize<MailMessage>` is the
whole job. The bodies come both ways a client sends them (`Text`, `Html`); `Headers` keeps every header's first value;
each attachment carries its bytes as base64 and its `ContentId` when the HTML places it inline. `Date` is null when the
message has none — never "now", which would be a fact the sender did not state.

**A bounce says which message failed.** A delivery-status report (RFC 3464) names the message it returns in its own
attached part, which is the one place a bounce reliably carries it — many carry no `In-Reply-To`. `FailedMessageIds`
holds those ids, for a FAILURE report only, so a receiver can mark exactly that delivery failed.

### Build writes what a provider sends   {#build}
`Mime.Build` takes the same JSON and answers the bytes: multipart when there are attachments or both bodies, every
header the message carries, its `Message-ID` and threading headers as given. It stamps no `Date` of its own — a build
is deterministic, so the same message builds to the same bytes, and a durable retry sends what the first try would have.

### Imap.Fetch and Smtp.Send move bytes, guarded   {#transport}
Both run over the host's mail transport, which refuses a private or loopback address exactly as `Http.*` does, requires
TLS (`993`/`465`) or upgrades with STARTTLS, and never logs a credential. The credential is an app password, or — with
`oauth: true` — an OAuth access token (XOAUTH2); either way it is a **secret sink**: pass a `Secret.X` or a setting
filled from one, never a literal. A host that refuses the login, is down or is not allowed is an
`ExternalServiceException` naming the host, never the credential.

- **`Imap.Fetch`** answers JSON `{ "Cursor": "…", "Notice": "…", "Messages": [{ "Uid": 42, "Raw": "<base64>" }] }`:
  the messages after `cursor` in `folder`, oldest first, at most `max`, read with PEEK (nothing is marked read). An
  EMPTY cursor answers no mail and the cursor of NOW — a mailbox connected today does not import its history. A cursor
  carries the folder's `UIDVALIDITY`; when the server re-creates the folder, the fetch starts again from now and
  `Notice` says so in a sentence an admin can read.
- **`Smtp.Send`** submits the bytes as they are — their `From`, recipients and `Message-ID` — and answers the
  server's reply.

A test stubs either like any effect: `Imap.Fetch.Stub((host, port, user, credential, oauth, folder, cursor, max) => …)`.

## Examples       {#examples}

Read a received message's subject and whether it is a bounce:

```osy title="parse a received message" test app=stdlib-mail-wire
class Seen {
  public string Subject = "";
  public List<string> FailedMessageIds = [];
}

string SubjectOf(byte[] raw) {
  return JsonSerializer.Deserialize<Seen>(Mime.Parse(raw)).Subject;
}

bool IsBounce(byte[] raw) {
  return JsonSerializer.Deserialize<Seen>(Mime.Parse(raw)).FailedMessageIds.Count > 0;
}
```

Build a plain message's bytes:

```osy title="build a message" test app=stdlib-mail-wire-build
class Outgoing {
  public string From = "";
  public List<string> To = [];
  public string Subject = "";
  public string Text = "";
}

byte[] Hello(string to) {
  return Mime.Build(JsonSerializer.Serialize(new Outgoing {
    From = "Acme <hello@acme.test>", To = [to], Subject = "Hello", Text = "Welcome aboard.",
  }));
}
```

## See also       {#see-also}
- [The Mail kit — send a message, keep a record, pick a sender](https://osysharp.com/reference/ui/mail-kit/) — `MailMessage`, the outbox, `ParseMail` and `RawOf`
- [Receiving mail — watched mailboxes (Gmail, Microsoft 365, IMAP)](https://osysharp.com/reference/ui/mail-receiving-kit/) — watched mailboxes; `ImapMailbox` is built on `Imap.Fetch` and `Smtp.Send`
- [Http.*](https://osysharp.com/reference/http/facade/) — the same egress guard, for HTTP
- [Zip.List — what an archive holds](https://osysharp.com/reference/stdlib/zip/) — what an attached archive holds
