Skip to main content

agentmail/client/
messages.rs

1use crate::client::NoBody;
2use crate::{Client, Error, Page, types::*, util::urlish};
3
4impl Client {
5    /// POST /v0/inboxes/{inbox_id}/messages/send
6    pub async fn send_message(
7        &self,
8        inbox_id: &str,
9        message: SendMessage,
10    ) -> Result<SentMessage, Error> {
11        self.request(
12            reqwest::Method::POST,
13            &format!("/v0/inboxes/{}/messages/send", urlish(inbox_id)),
14            &[],
15            Some(&message),
16        )
17        .await
18    }
19
20    /// GET /v0/inboxes/{inbox_id}/messages (first page; see
21    /// [`Client::list_messages_page`]).
22    pub async fn list_messages(&self, inbox_id: &str) -> Result<MessageList, Error> {
23        self.list_messages_page(inbox_id, Page::default()).await
24    }
25
26    /// GET /v0/inboxes/{inbox_id}/messages with pagination. Feed
27    /// [`MessageList::next_page_token`] back in as [`Page::page_token`]
28    /// until it comes back `None`.
29    pub async fn list_messages_page(
30        &self,
31        inbox_id: &str,
32        page: Page,
33    ) -> Result<MessageList, Error> {
34        self.request(
35            reqwest::Method::GET,
36            &format!("/v0/inboxes/{}/messages", urlish(inbox_id)),
37            &page.query(),
38            None::<&NoBody>,
39        )
40        .await
41    }
42
43    /// GET /v0/inboxes/{inbox_id}/messages/{message_id}
44    pub async fn get_message(&self, inbox_id: &str, message_id: &str) -> Result<Message, Error> {
45        self.request(
46            reqwest::Method::GET,
47            &format!(
48                "/v0/inboxes/{}/messages/{}",
49                urlish(inbox_id),
50                urlish(message_id),
51            ),
52            &[],
53            None::<&NoBody>,
54        )
55        .await
56    }
57
58    /// POST /v0/inboxes/{inbox_id}/messages/{message_id}/reply -- reply to a
59    /// message. The `to` field is derived from the parent message; only
60    /// `text`/`html`/`cc`/`bcc` are caller-supplied.
61    pub async fn reply_to_message(
62        &self,
63        inbox_id: &str,
64        message_id: &str,
65        reply: ReplyToMessage,
66    ) -> Result<SentMessage, Error> {
67        self.request(
68            reqwest::Method::POST,
69            &format!(
70                "/v0/inboxes/{}/messages/{}/reply",
71                urlish(inbox_id),
72                urlish(message_id),
73            ),
74            &[],
75            Some(&reply),
76        )
77        .await
78    }
79
80    /// POST /v0/inboxes/{inbox_id}/messages/{message_id}/reply-all -- reply
81    /// to all recipients of a message.
82    pub async fn reply_all_to_message(
83        &self,
84        inbox_id: &str,
85        message_id: &str,
86        reply: ReplyToMessage,
87    ) -> Result<SentMessage, Error> {
88        self.request(
89            reqwest::Method::POST,
90            &format!(
91                "/v0/inboxes/{}/messages/{}/reply-all",
92                urlish(inbox_id),
93                urlish(message_id),
94            ),
95            &[],
96            Some(&reply),
97        )
98        .await
99    }
100
101    /// PATCH /v0/inboxes/{inbox_id}/messages/{message_id} -- update a
102    /// message's labels (read state is a label, e.g. add/remove `unread`).
103    /// Returns the message id and its labels after the change, not the full
104    /// message body.
105    pub async fn update_message(
106        &self,
107        inbox_id: &str,
108        message_id: &str,
109        update: UpdateMessage,
110    ) -> Result<UpdatedMessage, Error> {
111        self.request(
112            reqwest::Method::PATCH,
113            &format!(
114                "/v0/inboxes/{}/messages/{}",
115                urlish(inbox_id),
116                urlish(message_id),
117            ),
118            &[],
119            Some(&update),
120        )
121        .await
122    }
123
124    /// DELETE /v0/inboxes/{inbox_id}/messages/{message_id} -- permanently
125    /// deletes a message.
126    pub async fn delete_message(&self, inbox_id: &str, message_id: &str) -> Result<(), Error> {
127        self.request(
128            reqwest::Method::DELETE,
129            &format!(
130                "/v0/inboxes/{}/messages/{}",
131                urlish(inbox_id),
132                urlish(message_id),
133            ),
134            &[],
135            None::<&NoBody>,
136        )
137        .await
138    }
139
140    /// GET /v0/inboxes/{inbox_id}/messages with filters and pagination.
141    /// Pass [`MessageListFilters`] with any filter fields set to narrow
142    /// results. Feed [`MessageList::next_page_token`] back in as
143    /// [`MessageListFilters::page_token`] until it comes back `None`.
144    pub async fn list_messages_filtered(
145        &self,
146        inbox_id: &str,
147        filters: MessageListFilters,
148    ) -> Result<MessageList, Error> {
149        self.request(
150            reqwest::Method::GET,
151            &format!("/v0/inboxes/{}/messages", urlish(inbox_id)),
152            &filters.query(),
153            None::<&NoBody>,
154        )
155        .await
156    }
157
158    /// GET /v0/inboxes/{inbox_id}/messages/search with filters. Feed
159    /// [`MessageList::next_page_token`] back in as
160    /// [`MessageListFilters::page_token`] until it comes back `None`.
161    pub async fn search_messages(&self, inbox_id: &str, query: &str) -> Result<MessageList, Error> {
162        self.search_messages_page(inbox_id, query, MessageListFilters::default())
163            .await
164    }
165
166    /// GET /v0/inboxes/{inbox_id}/messages/search with pagination.
167    pub async fn search_messages_page(
168        &self,
169        inbox_id: &str,
170        query: &str,
171        filters: MessageListFilters,
172    ) -> Result<MessageList, Error> {
173        let mut q = filters.query();
174        q.push(("q", query.to_string()));
175        self.request(
176            reqwest::Method::GET,
177            &format!("/v0/inboxes/{}/messages/search", urlish(inbox_id)),
178            &q,
179            None::<&NoBody>,
180        )
181        .await
182    }
183
184    /// POST /v0/inboxes/{inbox_id}/messages/{message_id}/forward, forward a
185    /// message. Reuses [`SendMessage`] for the new recipients and any added
186    /// body.
187    pub async fn forward_message(
188        &self,
189        inbox_id: &str,
190        message_id: &str,
191        message: SendMessage,
192    ) -> Result<SentMessage, Error> {
193        self.request(
194            reqwest::Method::POST,
195            &format!(
196                "/v0/inboxes/{}/messages/{}/forward",
197                urlish(inbox_id),
198                urlish(message_id),
199            ),
200            &[],
201            Some(&message),
202        )
203        .await
204    }
205
206    /// GET /v0/inboxes/{inbox_id}/messages/{message_id}/raw, a presigned URL for
207    /// the message's raw RFC 822 (`.eml`) source. Fetch the bytes with
208    /// [`Client::download_raw`].
209    pub async fn get_raw_message(
210        &self,
211        inbox_id: &str,
212        message_id: &str,
213    ) -> Result<RawMessage, Error> {
214        self.request(
215            reqwest::Method::GET,
216            &format!(
217                "/v0/inboxes/{}/messages/{}/raw",
218                urlish(inbox_id),
219                urlish(message_id),
220            ),
221            &[],
222            None::<&NoBody>,
223        )
224        .await
225    }
226
227    /// Download the raw `.eml` bytes from a [`RawMessage`]'s presigned URL,
228    /// without the API bearer token (the URL is already authenticated).
229    pub async fn download_raw(&self, raw: &RawMessage) -> Result<Vec<u8>, Error> {
230        let resp = self.http.get(&raw.download_url).send().await?;
231        let status = resp.status();
232        let bytes = resp.bytes().await?;
233        if !status.is_success() {
234            return Err(Error::Api {
235                status,
236                body: String::from_utf8_lossy(&bytes).into_owned(),
237            });
238        }
239        Ok(bytes.to_vec())
240    }
241
242    /// POST /v0/inboxes/{inbox_id}/messages/batch-get, fetch many messages by id
243    /// in one call.
244    pub async fn batch_get_messages(
245        &self,
246        inbox_id: &str,
247        message_ids: Vec<String>,
248    ) -> Result<BatchGetMessagesResponse, Error> {
249        self.request(
250            reqwest::Method::POST,
251            &format!("/v0/inboxes/{}/messages/batch-get", urlish(inbox_id)),
252            &[],
253            Some(&BatchGetMessages { message_ids }),
254        )
255        .await
256    }
257
258    /// POST /v0/inboxes/{inbox_id}/messages/batch-update, apply the same label
259    /// changes to many messages at once.
260    pub async fn batch_update_messages(
261        &self,
262        inbox_id: &str,
263        update: BatchUpdateMessages,
264    ) -> Result<BatchUpdateMessagesResponse, Error> {
265        self.request(
266            reqwest::Method::POST,
267            &format!("/v0/inboxes/{}/messages/batch-update", urlish(inbox_id)),
268            &[],
269            Some(&update),
270        )
271        .await
272    }
273}