unsent
unsent.dev
Get Started

Rust

Official Unsent Rust SDK for sending emails and managing contacts.

This guide shows how to install and use the official unsent Rust SDK.

Installation

Add the unsent crate to your Cargo.toml file:

[dependencies]
unsent = "1.0"

Initialize

Initialize the client with your API key.

use unsent::Client;

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let client = Client::new("un_xxx")?;
    Ok(())
}

Send an email

Now you can send an email. First, create an EmailsClient using your initialized client. Then, define an EmailCreate struct with your email details and pass it to the send method.

use unsent::{Client, types::EmailCreate, emails::EmailsClient};

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let client = Client::new("un_xxx")?;
    let emails = EmailsClient::new(&client);

    let email = EmailCreate {
        to: "user@example.com".to_string(),
        from: "no-reply@yourdomain.com".to_string(),
        subject: "Welcome".to_string(),
        html: Some("<strong>Hello!</strong>".to_string()),
        text: None,
        reply_to: None,
        cc: None,
        bcc: None,
        attachments: None,
        scheduled_at: None,
    };

    let response = emails.send(&email)?;
    println!("Email sent! ID: {}", response.id);
    
    Ok(())
}

With attachments and scheduling

You can also send emails with attachments and schedule them for later delivery.

  • Attachments: Create a vector of Attachment structs. Content must be Base64 encoded.
  • Scheduling: Use the scheduled_at field with an RFC 3339 formatted string.
use unsent::types::{EmailCreate, Attachment};
use chrono::{Utc, Duration};

// Calculate a time 10 minutes from now
let scheduled_time = (Utc::now() + Duration::minutes(10)).to_rfc3339();

let email = EmailCreate {
    to: "user@example.com".to_string(),
    from: "no-reply@yourdomain.com".to_string(),
    subject: "Report".to_string(),
    text: Some("See attached.".to_string()),
    attachments: Some(vec![
        Attachment {
            filename: "report.txt".to_string(),
            content: "SGVsbG8gd29ybGQ=".to_string(), // Content must be Base64 encoded
        }
    ]),
    scheduled_at: Some(scheduled_time),
    html: None,
    reply_to: None,
    cc: None,
    bcc: None,
};

let response = emails.create(&email)?;

Batch send

To send multiple emails efficiently in a single request, use the batch method with a vector of EmailBatchItem structs.

use unsent::types::EmailBatchItem;

let batch = vec![
    EmailBatchItem {
        to: "a@example.com".to_string(),
        from: "no-reply@yourdomain.com".to_string(),
        subject: "A".to_string(),
        html: Some("<p>A</p>".to_string()),
        text: None,
        attachments: None,
        scheduled_at: None,
    },
    EmailBatchItem {
        to: "b@example.com".to_string(),
        from: "no-reply@yourdomain.com".to_string(),
        subject: "B".to_string(),
        html: Some("<p>B</p>".to_string()),
        text: None,
        attachments: None,
        scheduled_at: None,
    },
];

let response = emails.batch(&batch)?;
println!("Sent {} emails", response.emails.len());

Idempotent Retries

To safely retry requests, you can use RequestOptions with the _with_options methods.

use unsent::types::RequestOptions;

let options = RequestOptions::new().with_idempotency_key("unique-key-123");

// For single email
emails.send_with_options(&email, &options)?;

// For batch emails
emails.batch_with_options(&batch, &options)?;

Retrieve and manage emails

You can retrieve details of sent emails, update scheduled emails, or cancel them.

Get an email

Fetch the status and details of a specific email using its ID:

let email = emails.get("email_123")?;
println!("Email status: {}", email.status);

Update schedule time

Change the scheduled delivery time for a pending email:

use unsent::types::EmailUpdate;
use chrono::{Utc, Duration};

let new_time = (Utc::now() + Duration::hours(1)).to_rfc3339();

let update = EmailUpdate {
    scheduled_at: Some(new_time),
    subject: None,
    html: None,
    text: None,
    attachments: None,
};

let response = emails.update("email_123", &update)?;

Cancel a scheduled email

Cancel a scheduled email so it won't be sent:

let response = emails.cancel("email_123")?;
println!("Email cancelled successfully");

Contacts

Manage your contacts and organize them into contact books. All contact operations require a contact book ID.

Create a contact

Add a new contact to a specific contact book. You can include custom metadata using a HashMap.

use unsent::{types::ContactCreate, contacts::ContactsClient};
use std::collections::HashMap;

let client = Client::new("un_xxx")?;
let contacts = ContactsClient::new(&client);

let mut metadata = HashMap::new();
metadata.insert("plan".to_string(), serde_json::json!("pro"));

let contact = ContactCreate {
    email: "user@example.com".to_string(),
    first_name: Some("Jane".to_string()),
    last_name: None,
    metadata: Some(metadata),
};

let response = contacts.create("book_123", &contact)?;

Get a contact

Retrieve a contact's details:

let contact = contacts.get("book_123", "contact_456")?;

Update a contact

Update a contact's information:

use unsent::types::ContactUpdate;

let mut metadata = HashMap::new();
metadata.insert("plan".to_string(), serde_json::json!("enterprise"));

let update = ContactUpdate {
    first_name: Some("John".to_string()),
    last_name: None,
    metadata: Some(metadata),
};

let response = contacts.update("book_123", "contact_456", &update)?;

Upsert a contact

Use upsert to create a contact if they don't exist, or update them if they do.

use unsent::types::ContactUpsert;

let upsert = ContactUpsert {
    email: "user@example.com".to_string(),
    first_name: Some("Jane".to_string()),
    last_name: None,
    metadata: None,
};

let response = contacts.upsert("book_123", "contact_456", &upsert)?;

Delete a contact

Remove a contact from a book:

let response = contacts.delete("book_123", "contact_456")?;

Campaigns

Campaigns allow you to send emails to all contacts in a specific contact book.

Create a campaign

Create a new campaign by defining its content and the target contact book.

use unsent::{types::CampaignCreate, campaigns::CampaignsClient};

let client = Client::new("un_xxx")?;
let campaigns = CampaignsClient::new(&client);

let campaign = CampaignCreate {
    name: "Welcome Series".to_string(),
    subject: "Welcome!".to_string(),
    html: "<p>Thanks for joining us!</p>".to_string(),
    from: "welcome@yourdomain.com".to_string(),
    contact_book_id: "book_123".to_string(),
};

let response = campaigns.create(&campaign)?;
println!("Campaign created! ID: {}", response.id);

Schedule a campaign

Schedule the campaign to be sent at a specific time.

use unsent::types::CampaignSchedule;

let schedule = CampaignSchedule {
    scheduled_at: "2024-12-01T10:00:00Z".to_string(),
};

let response = campaigns.schedule(&campaign.id, &schedule)?;

Pause and resume campaigns

Control the delivery of your campaign.

// Pause
let pause_resp = campaigns.pause("campaign_123")?;
println!("Campaign paused successfully!");

// Resume
let resume_resp = campaigns.resume("campaign_123")?;
println!("Campaign resumed successfully!");

Domains

Manage your sending domains.

List domains

Get a list of all domains and their statuses.

use unsent::domains::DomainsClient;

let client = Client::new("un_xxx")?;
let domains = DomainsClient::new(&client);

let domain_list = domains.list()?;
for domain in domain_list {
    println!("Domain: {}, Status: {}", domain.domain, domain.status);
}

Create a domain

Register a new domain for sending.

use unsent::types::DomainCreate;

let domain = DomainCreate {
    domain: "yourdomain.com".to_string(),
};

let response = domains.create(&domain)?;

Verify a domain

Check the verification status of a domain.

let response = domains.verify(123)?;
println!("Verification status: {}", response.status);

Error handling

The SDK uses Rust's Result type for error handling. You can match on the result to handle success or failure.

use unsent::{Client, UnsentError};

let client = Client::new("un_xxx")?;

match emails.send(&email) {
    Ok(response) => println!("Email sent! ID: {}", response.id),
    Err(UnsentError::Api(api_error)) => {
        // Handle API errors (e.g., invalid API key, validation errors)
        eprintln!("API Error: {} - {}", api_error.code, api_error.message);
    }
    Err(e) => eprintln!("Error: {}", e),
}

Disable Automatic Error Raising

If you prefer not to have the client return an error for non-2xx responses (though in Rust, Result is idiomatic), you can configure the client:

let client = Client::new("un_xxx")?.with_raise_on_error(false);

Custom HTTP client

For advanced use cases, such as adding custom timeouts or proxies, you can provide your own reqwest client.

use reqwest::blocking::Client as HttpClient;
use std::time::Duration;

// Create a custom HTTP client with a timeout
let http_client = HttpClient::builder()
    .timeout(Duration::from_secs(30))
    .build()?;

// Initialize the SDK with the custom client
let client = Client::new("un_xxx")?.with_http_client(http_client);