Messages
A message belongs to an inbox. Send messages by referencing the inbox you're sending from, and receive messages by listing or subscribing to an inbox. Threading, attachments, and delivery status are handled by the API.
Send a message
A single call queues the message and returns the id plus initial delivery_status: "pending". The from address must reference an inbox you own.
import { Clawise } from "clawise";
const clawise = new Clawise({ apiKey: "..." });
const msg = await clawise.mail.messages.send({
from: "[email protected]",
to: ["[email protected]"],
subject: "Welcome to Acme",
body_html: "<p>Hi Alice — your account is ready.</p>",
body_text: "Hi Alice — your account is ready.",
});
console.log(msg.id, msg.delivery_status);from clawise import Clawise
clawise = Clawise(api_key="...")
msg = clawise.mail.messages.send(
from_address="[email protected]",
to=["[email protected]"],
subject="Welcome to Acme",
body_html="<p>Hi Alice — your account is ready.</p>",
body_text="Hi Alice — your account is ready.",
)msg, _ := client.Mail.Messages.Send(ctx, &clawise.MessageCreateParams{
From: "[email protected]",
To: []string{"[email protected]"},
Subject: "Welcome to Acme",
BodyHTML: clawise.String("<p>Hi Alice — your account is ready.</p>"),
BodyText: clawise.String("Hi Alice — your account is ready."),
})clawise mail message send \
--from [email protected] \
--to [email protected] \
--subject "Welcome to Acme" \
--body "Hi Alice — your account is ready." \
--body-html "<p>Hi Alice — your account is ready.</p>"Receive messages
Two ways to read incoming messages: poll the list endpoint on your own schedule, or register a webhook for push delivery. Both surface the same message resource.
Polling
List messages in an inbox, paginated (default 20 per page; SDK iterators auto-fetch the next page). For incremental polling, pass after=<last seen timestamp> so each call returns only new messages. Other filters: direction, label, thread_id, before, ascending.
for await (const msg of clawise.mail.messages.list({ email: "[email protected]" })) {
console.log(msg.id, msg.subject);
}
const full = await clawise.mail.messages.get("msg_01j9x...");
console.log(full.body_text);for msg in clawise.mail.messages.list(email="[email protected]"):
print(msg.id, msg.subject)
full = clawise.mail.messages.get("msg_01j9x...")
print(full.body_text)iter := client.Mail.Messages.List(ctx, &clawise.MessageListParams{
Email: "[email protected]",
})
for iter.Next() {
msg := iter.Current()
fmt.Println(msg.ID, msg.Subject)
}
full, _ := client.Mail.Messages.Get(ctx, "msg_01j9x...")
fmt.Println(full.BodyText)Webhooks
For real-time delivery, register an endpoint subscribed to mail.message.received — your server receives a signed POST shortly after each new message. See Receiving events for endpoint setup and signature verification.
Attachments
Upload first, reference by ID on send. The same attachment can be reused across multiple sends without re-uploading.
import { readFile } from "node:fs/promises";
const buffer = await readFile("invoice.pdf");
const att = await clawise.mail.attachments.upload({
email: "[email protected]",
file: new Blob([buffer], { type: "application/pdf" }),
filename: "invoice.pdf",
});
await clawise.mail.messages.send({
from: "[email protected]",
to: ["[email protected]"],
subject: "Your invoice",
body_html: "<p>Your invoice is attached.</p>",
attachment_ids: [att.id],
});with open("invoice.pdf", "rb") as f:
att = clawise.mail.attachments.upload(
file=f,
filename="invoice.pdf",
email="[email protected]",
)
clawise.mail.messages.send(
from_address="[email protected]",
to=["[email protected]"],
subject="Your invoice",
body_html="<p>Your invoice is attached.</p>",
attachment_ids=[att.id],
)f, _ := os.Open("invoice.pdf")
defer f.Close()
att, _ := client.Mail.Attachments.Upload(ctx, &clawise.AttachmentUploadParams{
File: f,
Filename: "invoice.pdf",
InboxEmail: "[email protected]",
})
client.Mail.Messages.Send(ctx, &clawise.MessageCreateParams{
From: "[email protected]",
To: []string{"[email protected]"},
Subject: "Your invoice",
BodyHTML: clawise.String("<p>Your invoice is attached.</p>"),
AttachmentIDs: []string{att.ID},
})Inlining binary blobs in JSON wastes bandwidth and hits HTTP body limits. Two-step upload also makes retry safe — if the send fails, the attachment is already persisted.
Delivery status
Every outbound message moves through a state machine. The current state lives in delivery_status; each transition fires a webhook event.
| Status | Meaning | Webhook event |
|---|---|---|
pending | Queued by Clawise; not yet sent | (set on creation) |
accepted | Accepted by recipient mail server for delivery | mail.message.sent |
delivered | Recipient mail server confirmed | mail.message.delivered |
bounced | Hard bounce — address invalid | mail.message.bounced |
complained | Recipient marked as spam | mail.message.complained |
failed | Rejected by recipient mail server, or retries exhausted | mail.message.failed |
delivered, bounced, complained, failed are terminal — no further transitions. A message can jump directly to failed from any earlier state if rejected outright.
Extract OTPs
For OTP capture, match against body_text with a regex. Combine with Inboxes for end-to-end signup automation.
for await (const msg of clawise.mail.messages.list({ email: "[email protected]" })) {
const match = (msg.body_text ?? "").match(/\b(\d{6})\b/);
if (match) {
console.log("OTP:", match[1]);
break;
}
}import re
for msg in clawise.mail.messages.list(email="[email protected]"):
if m := re.search(r"\b(\d{6})\b", msg.body_text or ""):
print("OTP:", m.group(1))
breakre := regexp.MustCompile(`\b(\d{6})\b`)
iter := client.Mail.Messages.List(ctx, &clawise.MessageListParams{
Email: "[email protected]",
})
for iter.Next() {
msg := iter.Current()
if m := re.FindStringSubmatch(msg.BodyText); len(m) > 1 {
fmt.Println("OTP:", m[1])
break
}
}