# Axum

> Send your first email from an Axum handler using reqwest against the SendPing API.

Rust has an official SDK — the [`sendping`](https://crates.io/crates/sendping) crate on crates.io — and an [Axum](https://github.com/tokio-rs/axum) handler can call it directly on the [Tokio](https://tokio.rs) runtime. The example below instead takes the raw-HTTP route, POSTing JSON to `https://www.sendping.co/api/emails` with the async [`reqwest`](https://crates.io/crates/reqwest) client, so you can see exactly what goes over the wire.

## Prerequisites

- A SendPing [API key](https://www.sendping.co/docs/api-keys/overview).
- A [verified domain](https://www.sendping.co/docs/domains/managing) to send from.

## 1. Install dependencies

Add Axum, an async HTTP client, a runtime, and JSON support to your Cargo project.

```bash
cargo add axum
cargo add reqwest -F json
cargo add tokio -F macros,rt-multi-thread
cargo add serde_json
```

## 2. Set your API key

**.env**

```toml
SENDPING_API_KEY=mb_xxxxxxxxx
```

## 3. Send an email

Build an Axum router with a single route whose handler POSTs the email and returns the created `id`.

**src/main.rs**

```rust
use axum::{http::StatusCode, routing::get, Router};
use serde_json::json;

#[tokio::main]
async fn main() {
    let app = Router::new().route("/", get(endpoint));

    let listener = tokio::net::TcpListener::bind("0.0.0.0:3000")
        .await
        .unwrap();
    axum::serve(listener, app).await.unwrap();
}

async fn endpoint() -> Result<String, StatusCode> {
    let api_key = std::env::var("SENDPING_API_KEY")
        .map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?;

    let res = reqwest::Client::new()
        .post("https://www.sendping.co/api/emails")
        .bearer_auth(api_key)
        // reqwest sends no User-Agent of its own and the API rejects a request
        // without one with 403 validation_error, so set it explicitly.
        .header("User-Agent", "my-app/1.0")
        .json(&json!({
            "from": "Acme <onboarding@yourdomain.com>",
            "to": ["delivered@test.sendping.co"],
            "subject": "Hello World",
            "html": "<strong>It works!</strong>",
        }))
        .send()
        .await
        .map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?;

    let body: serde_json::Value = res
        .json()
        .await
        .map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?;

    Ok(body["id"].to_string())
}
```

Opening your browser at `http://localhost:3000` (or running `curl localhost:3000`) sends an email and returns its `id`.

> **Warning:** The explicit `User-Agent` header is required: `reqwest` does not set one by default and every SendPing API request without a `User-Agent` is rejected with `403 validation_error`. The official crate sets it for you.

> **Note:** See [Send an email](https://www.sendping.co/docs/api/emails-send) for the full body schema — `cc`, `bcc`, `reply_to`, `attachments`, and more.
