diff --git a/Cargo.toml b/Cargo.toml
index 88ad9b9..8383d81 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -6,6 +6,7 @@ authors = ["Antfroze"]
description = "Async Rust client for the Plunk transactional email API"
license = "MIT"
repository = "https://git.coldspire.com/Antfroze/plunk-rs"
+readme = "README.md"
keywords = ["plunk", "email", "transactional", "api"]
categories = ["api-bindings", "email"]
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..ede7568
--- /dev/null
+++ b/README.md
@@ -0,0 +1,121 @@
+
+
+# plunk-rs
+
+**An async Rust client for [Plunk](https://www.useplunk.com/) transactional email.**
+
+*Send HTML, templates, and template variables (the `data` object) with validation, typed errors, and a small dependency footprint (reqwest + rustls).*
+
+[](./LICENSE)
+[](https://crates.io/crates/plunk-rs)
+[](https://docs.rs/plunk-rs)
+
+[Repository](https://git.coldspire.com/Antfroze/plunk-rs) · [Plunk API](https://docs.useplunk.com)
+
+
+
+---
+
+## Why this crate
+
+| | |
+| :--- | :--- |
+| **Focused** | Covers `POST /v1/send`—HTML and template sends, with optional `from`, `reply`, headers, and `data` for template variables. |
+| **Validates early** | `Email` and `EmailAddress` are built with checks so bad requests fail before the wire. |
+| **Clean boundary** | Wire DTOs stay internal; your code uses `Email`, `SendResponse`, and `Error`. |
+| **TLS** | [reqwest](https://crates.io/crates/reqwest) with **rustls**—no OpenSSL at build time. |
+
+## Installation
+
+Add to `Cargo.toml`:
+
+```toml
+[dependencies]
+plunk-rs = "0.1"
+tokio = { version = "1", features = ["rt-multi-thread", "macros"] }
+```
+
+Or:
+
+```text
+cargo add plunk-rs
+cargo add tokio --features rt-multi-thread,macros
+```
+
+## Quick start
+
+```rust
+use plunk_rs::{Client, Email, EmailAddress};
+
+#[tokio::main]
+async fn main() -> Result<(), plunk_rs::Error> {
+ let client = Client::new("sk_your_plunk_api_key")?;
+
+ let email = Email::html("user@example.com", "Welcome", "Hello
")?
+ .from(EmailAddress::named("My app", "hello@yourdomain.com")?)?
+ .reply_to("support@yourdomain.com")?;
+
+ let response = client.send(&email).await?;
+
+ for delivery in response.deliveries() {
+ println!("{} → {}", delivery.contact().email(), delivery.id());
+ }
+ Ok(())
+}
+```
+
+**Template send** (with data for the template):
+
+```rust
+use plunk_rs::{Client, Email};
+use serde_json::json;
+
+#[tokio::main]
+async fn main() -> Result<(), plunk_rs::Error> {
+ let client = Client::new("sk_your_plunk_api_key")?;
+
+ let email = Email::template("user@example.com", "tpl_xxxxxxxx")?
+ .with_data(json!({ "first_name": "Ada" }))?;
+
+ client.send(&email).await?;
+ Ok(())
+}
+```
+
+## Self-hosted or custom base URL
+
+```rust
+let client = Client::builder("sk_xxx")
+ .base_url("https://plunk.example.com/api")?
+ .build()?;
+```
+
+`base_url` is normalized: if the path does not end with `/`, one is added so `v1/send` joins correctly.
+
+## API surface
+
+- **`Client` / `ClientBuilder`** — Bearer API key, optional `reqwest::Client` injection.
+- **`Email`**, **`EmailAddress`**, **`Recipients`** — Construction helpers for HTML vs template, headers, and JSON `data` (must serialize to a JSON object for templates).
+- **`SendResponse`**, **`Delivery`**, **`Contact`** — Parsed success payload from the send response.
+- **`Error`**, **`ApiError`**, **`Result`** — Transport, validation, and Plunk error bodies (with status and raw body preserved).
+
+## Requirements
+
+Rust **2024** edition (use a current stable toolchain that supports it). Async code assumes a runtime such as **Tokio**.
+
+## Docs
+
+- [**docs.rs** — plunk-rs](https://docs.rs/plunk-rs)
+- [Plunk documentation](https://docs.useplunk.com)
+
+## License
+
+[MIT](LICENSE)
+
+---
+
+
+
+Made for sending mail through [Plunk](https://www.useplunk.com/) without the ceremony.
+
+