1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
use reqwest::Method;
use crate::api::{decode_response, ErrorKind};
use crate::client::Client;
use crate::error::Error;
use crate::models::bulks::{
BulkResponse, BulkStatusResponse, RescheduleBulkRequest, UpdateBulkStatusRequest,
};
impl Client {
/// Fetches the scheduled dispatch time of a bulk.
///
/// Wraps `GET /sms/1/bulks?bulkId=...`. The bulk ID is either one
/// you set explicitly via
/// [`crate::models::send::RequestSchedulingSettings::bulk_id`] or
/// the value Infobip auto-assigned, returned in
/// [`crate::models::send::SmsResponse::bulk_id`].
///
/// # Errors
///
/// On non-2xx responses, returns
/// [`Error::Exception`]. Notably, a
/// bulk that has already finished or never existed yields HTTP
/// 404.
pub async fn get_scheduled_bulk(&self, bulk_id: &str) -> Result<BulkResponse, Error> {
let response = self
.request(Method::GET, "sms/1/bulks")?
.query(&[("bulkId", bulk_id)])
.send()
.await?;
decode_response(response, ErrorKind::Legacy).await
}
/// Reschedules a pending bulk.
///
/// Wraps `PUT /sms/1/bulks?bulkId=...`. Only bulks in `PENDING`
/// status can be rescheduled.
///
/// # Errors
///
/// On non-2xx responses, returns
/// [`Error::Exception`].
///
/// # Example
///
/// ```no_run
/// # use infobip_sms::Client;
/// use infobip_sms::models::bulks::RescheduleBulkRequest;
///
/// # async fn run(client: Client, bulk_id: &str) -> Result<(), infobip_sms::Error> {
/// client
/// .reschedule_bulk(
/// bulk_id,
/// &RescheduleBulkRequest { send_at: "2026-12-24T09:00:00.000+0000".into() },
/// )
/// .await?;
/// # Ok(()) }
/// ```
pub async fn reschedule_bulk(
&self,
bulk_id: &str,
request: &RescheduleBulkRequest,
) -> Result<BulkResponse, Error> {
let response = self
.request(Method::PUT, "sms/1/bulks")?
.query(&[("bulkId", bulk_id)])
.json(request)
.send()
.await?;
decode_response(response, ErrorKind::Legacy).await
}
/// Fetches the lifecycle status of a bulk.
///
/// Wraps `GET /sms/1/bulks/status?bulkId=...`. Returns one of the
/// [`BulkStatus`](crate::models::common::BulkStatus) values.
///
/// # Errors
///
/// On non-2xx responses, returns
/// [`Error::Exception`].
pub async fn get_bulk_status(&self, bulk_id: &str) -> Result<BulkStatusResponse, Error> {
let response = self
.request(Method::GET, "sms/1/bulks/status")?
.query(&[("bulkId", bulk_id)])
.send()
.await?;
decode_response(response, ErrorKind::Legacy).await
}
/// Pauses, resumes, or cancels a bulk.
///
/// Wraps `PUT /sms/1/bulks/status?bulkId=...`. Allowed transitions:
///
/// - `Pending` → `Paused` (pause)
/// - `Paused` → `Pending` (resume)
/// - `Pending` or `Paused` → `Canceled` (terminal)
///
/// # Errors
///
/// On non-2xx responses, returns
/// [`Error::Exception`]. Disallowed
/// transitions yield HTTP 400.
///
/// # Example
///
/// ```no_run
/// # use infobip_sms::Client;
/// use infobip_sms::models::bulks::UpdateBulkStatusRequest;
/// use infobip_sms::models::common::BulkStatus;
///
/// # async fn run(client: Client, bulk_id: &str) -> Result<(), infobip_sms::Error> {
/// client
/// .update_bulk_status(bulk_id, &UpdateBulkStatusRequest { status: BulkStatus::Canceled })
/// .await?;
/// # Ok(()) }
/// ```
pub async fn update_bulk_status(
&self,
bulk_id: &str,
request: &UpdateBulkStatusRequest,
) -> Result<BulkStatusResponse, Error> {
let response = self
.request(Method::PUT, "sms/1/bulks/status")?
.query(&[("bulkId", bulk_id)])
.json(request)
.send()
.await?;
decode_response(response, ErrorKind::Legacy).await
}
}