infobip-sms-sdk 0.1.0

Async Rust SDK for the Infobip SMS API: send messages, manage scheduled bulks, query delivery reports and logs, fetch inbound SMS, and parse webhook payloads.
Documentation

infobip-sms-sdk

Crates.io Documentation License

Async Rust SDK for the Infobip SMS API.

  • Send text or binary SMS to one or many recipients in a single call.
  • Schedule, pause, resume, reschedule, or cancel bulk sends.
  • Pull delivery reports, message logs, and inbound messages when you can't expose a webhook.
  • Parse the payloads Infobip POSTs to your webhook endpoints.
  • All four official auth schemes: API key, HTTP Basic, IBSSO token, OAuth2 bearer.

Installation

[dependencies]
infobip-sms-sdk = "0.1"
tokio = { version = "1", features = ["macros", "rt-multi-thread"] }

Quick start

use infobip_sms::{Auth, Client};
use infobip_sms::models::send::{
    SmsMessage, SmsMessageContent, SmsRequest, SmsTextMessageContent, SmsToDestination,
};

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let client = Client::builder()
        .base_url("https://xxxxx.api.infobip.com")
        .auth(Auth::api_key("YOUR_API_KEY"))
        .build()?;

    let response = client
        .send_messages(&SmsRequest {
            messages: vec![SmsMessage {
                sender: Some("InfoSMS".into()),
                destinations: vec![SmsToDestination {
                    to: "41793026727".into(),
                    ..Default::default()
                }],
                content: SmsMessageContent::Text(SmsTextMessageContent {
                    text: "Hello from Rust!".into(),
                    ..Default::default()
                }),
                ..Default::default()
            }],
            options: None,
        })
        .await?;

    println!("Bulk ID: {:?}", response.bulk_id);
    Ok(())
}

Your account base URL is shown in the Infobip portal — usually something like https://xxxxx.api.infobip.com.

Endpoint coverage

Only the latest version of every endpoint is exposed. Deprecated v1 and v2 send / log / report endpoints are intentionally omitted.

Method Path Client method
POST /sms/3/messages send_messages
POST /sms/1/preview preview_message
GET /sms/3/reports get_delivery_reports
GET /sms/3/logs get_logs
GET /sms/1/inbox/reports get_inbound_messages
GET /sms/1/bulks get_scheduled_bulk
PUT /sms/1/bulks reschedule_bulk
GET /sms/1/bulks/status get_bulk_status
PUT /sms/1/bulks/status update_bulk_status
POST /ct/1/log/end/{messageId} end_conversion_log

Webhook payload shapes (delivery reports, inbound SMS, click tracking) live in models::webhooks and are ready to deserialize with serde_json::from_str.

Authentication

Pick the scheme that matches what your account is provisioned for:

use infobip_sms::Auth;

// `Authorization: App <key>` — recommended for service-to-service.
let _ = Auth::api_key("YOUR_API_KEY");

// HTTP Basic auth.
let _ = Auth::basic("username", "password");

// `Authorization: IBSSO <token>` — Infobip Single Sign-On.
let _ = Auth::ibsso("YOUR_IBSSO_TOKEN");

// `Authorization: Bearer <token>` — OAuth2 client credentials.
let _ = Auth::bearer("YOUR_OAUTH2_TOKEN");

More examples

Schedule a bulk and pause it

use infobip_sms::models::bulks::UpdateBulkStatusRequest;
use infobip_sms::models::common::BulkStatus;
use infobip_sms::models::send::{RequestSchedulingSettings, SmsRequest, SmsRequestOptions};

# async fn run(client: infobip_sms::Client) -> Result<(), infobip_sms::Error> {
let response = client
    .send_messages(&SmsRequest {
        messages: vec![/* ... */],
        options: Some(SmsRequestOptions {
            schedule: Some(RequestSchedulingSettings {
                bulk_id: Some("BULK-ID-123".into()),
                send_at: Some("2026-12-24T09:00:00.000+0000".into()),
                sending_speed_limit: None,
            }),
            ..Default::default()
        }),
    })
    .await?;

let bulk_id = response.bulk_id.unwrap();

// Later: pause it.
client
    .update_bulk_status(&bulk_id, &UpdateBulkStatusRequest { status: BulkStatus::Paused })
    .await?;
# Ok(()) }

Page through message logs

use infobip_sms::models::logs::LogsQuery;

# async fn run(client: infobip_sms::Client) -> Result<(), infobip_sms::Error> {
let mut query = LogsQuery {
    use_cursor: Some(true),
    limit: Some(1000),
    ..Default::default()
};

loop {
    let page = client.get_logs(&query).await?;
    for log in page.results {
        // process `log`
    }
    match page.cursor.and_then(|c| c.next_cursor) {
        Some(next) => query.cursor = Some(next),
        None => break,
    }
}
# Ok(()) }

Parse a delivery-report webhook

use infobip_sms::models::webhooks::DeliveryReportPayload;

let body = r#"{"results":[]}"#; // raw POST body
let parsed: DeliveryReportPayload = serde_json::from_str(body)?;
# Ok::<(), serde_json::Error>(())

A runnable end-to-end send sample lives in examples/send_basic.rs.

Error handling

Every fallible call returns Result<T, infobip_sms::Error>. The two main variants you'll match on:

Variant When you see it Body type
Error::Api v3 endpoints (/sms/3/*) ApiError
Error::Exception v1 endpoints (/sms/1/*, /ct/1/log/end/...) ApiException

Both carry the HTTP status code. Error::Unexpected preserves the raw body when the response doesn't match either schema, and Error::Http / Error::Url / Error::Json cover transport, URL parsing, and (de)serialization failures respectively.

TLS

The crate links reqwest with rustls-tls by default. If you need native-tls instead — or any other reqwest customization (proxies, custom DNS, shared connection pools) — build a reqwest::Client yourself and pass it in via ClientBuilder::http_client.

Date-times

Time values (sentAt, doneAt, receivedAt, sendAt, …) are kept as String to avoid pulling in a date-time dependency. The wire format is yyyy-MM-dd'T'HH:mm:ss.SSSZ (ISO 8601 with millisecond precision and a numeric offset). Parse them into chrono::DateTime or time::OffsetDateTime in your own code if you need them typed.

MSRV

Edition 2024. The crate tracks the latest stable Rust release.

Documentation

Full API reference: https://docs.rs/infobip-sms-sdk.

License

Licensed under either of

at your option.

Contributing

Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual-licensed as above, without any additional terms or conditions.

This crate is community-maintained and is not affiliated with or endorsed by Infobip.