@@ -37,14 +37,19 @@ async def main():
3737from commune .types import (
3838 Attachment ,
3939 DeleteResult ,
40+ DeliveryEvent ,
41+ DeliveryMetrics ,
42+ DeliverySuppression ,
4043 DomainDnsRecord ,
4144 DomainVerificationResult ,
4245 CreateDomainPayload ,
4346 CreateInboxPayload ,
4447 UpdateInboxPayload ,
4548 SetInboxWebhookPayload ,
49+ SearchResult ,
4650 SendMessagePayload ,
4751 SendMessageResult ,
52+ SmsSendResult ,
4853 UploadAttachmentPayload ,
4954 AttachmentUpload ,
5055 AttachmentUrl ,
@@ -766,6 +771,168 @@ async def url(self, attachment_id: str, *, expires_in: int = 3600) -> Attachment
766771 return AttachmentUrl .model_validate (data )
767772
768773
774+ class _AsyncSearch :
775+ """Async full-text search across email threads."""
776+
777+ def __init__ (self , http : AsyncHttpClient ):
778+ self ._http = http
779+
780+ async def threads (
781+ self ,
782+ query : str ,
783+ * ,
784+ inbox_id : str | None = None ,
785+ domain_id : str | None = None ,
786+ limit : int = 20 ,
787+ ) -> list [SearchResult ]:
788+ """Search across email threads by subject or content.
789+
790+ Args:
791+ query: Search query string.
792+ inbox_id: Scope search to a single inbox (recommended).
793+ domain_id: Scope search to a domain.
794+ limit: Max results (1–100, default 20).
795+
796+ Returns:
797+ List of SearchResult objects ordered by relevance.
798+ """
799+ params : dict [str , Any ] = {"q" : query , "limit" : limit }
800+ if inbox_id :
801+ params ["inbox_id" ] = inbox_id
802+ if domain_id :
803+ params ["domain_id" ] = domain_id
804+ data = await self ._http .get ("/v1/search/threads" , params = params )
805+ if isinstance (data , list ):
806+ return [SearchResult .model_validate (r ) for r in data ]
807+ return [SearchResult .model_validate (r ) for r in (data or [])]
808+
809+
810+ class _AsyncSms :
811+ """Async SMS sending."""
812+
813+ def __init__ (self , http : AsyncHttpClient ):
814+ self ._http = http
815+
816+ async def send (
817+ self ,
818+ * ,
819+ to : str ,
820+ body : str ,
821+ phone_number_id : str | None = None ,
822+ ) -> SmsSendResult :
823+ """Send an SMS message.
824+
825+ Args:
826+ to: Recipient phone number in E.164 format (e.g. "+15551234567").
827+ body: SMS message text.
828+ phone_number_id: Send from a specific provisioned number (optional).
829+
830+ Returns:
831+ SmsSendResult with .message_id, .status, .credits_charged.
832+ """
833+ payload : dict [str , Any ] = {"to" : to , "body" : body }
834+ if phone_number_id :
835+ payload ["phone_number_id" ] = phone_number_id
836+ data = await self ._http .post ("/v1/sms/send" , json = payload )
837+ return SmsSendResult .model_validate (data )
838+
839+
840+ class _AsyncDelivery :
841+ """Async deliverability monitoring."""
842+
843+ def __init__ (self , http : AsyncHttpClient ):
844+ self ._http = http
845+
846+ async def metrics (
847+ self ,
848+ * ,
849+ inbox_id : str | None = None ,
850+ domain_id : str | None = None ,
851+ period : str = "7d" ,
852+ ) -> DeliveryMetrics :
853+ """Get email deliverability metrics.
854+
855+ Args:
856+ inbox_id: Filter metrics to a specific inbox.
857+ domain_id: Filter metrics to a domain.
858+ period: Time window — "24h", "7d" (default), or "30d".
859+
860+ Returns:
861+ DeliveryMetrics with sent, delivered, bounced, complained, failed counts
862+ and delivery_rate, bounce_rate, complaint_rate percentages.
863+ """
864+ params : dict [str , Any ] = {"period" : period }
865+ if inbox_id :
866+ params ["inbox_id" ] = inbox_id
867+ if domain_id :
868+ params ["domain_id" ] = domain_id
869+ data = await self ._http .get ("/v1/delivery/metrics" , params = params )
870+ return DeliveryMetrics .model_validate (data )
871+
872+ async def suppressions (
873+ self ,
874+ * ,
875+ inbox_id : str | None = None ,
876+ domain_id : str | None = None ,
877+ limit : int = 50 ,
878+ ) -> list [DeliverySuppression ]:
879+ """List suppressed email addresses.
880+
881+ Args:
882+ inbox_id: Filter suppressions to a specific inbox.
883+ domain_id: Filter suppressions to a domain.
884+ limit: Max results (default 50).
885+
886+ Returns:
887+ List of DeliverySuppression objects with .email, .reason, .suppressed_at.
888+ """
889+ params : dict [str , Any ] = {"limit" : limit }
890+ if inbox_id :
891+ params ["inbox_id" ] = inbox_id
892+ if domain_id :
893+ params ["domain_id" ] = domain_id
894+ data = await self ._http .get ("/v1/delivery/suppressions" , params = params )
895+ if isinstance (data , list ):
896+ return [DeliverySuppression .model_validate (s ) for s in data ]
897+ return [DeliverySuppression .model_validate (s ) for s in (data or [])]
898+
899+ async def events (
900+ self ,
901+ * ,
902+ message_id : str | None = None ,
903+ inbox_id : str | None = None ,
904+ domain_id : str | None = None ,
905+ event_type : str | None = None ,
906+ limit : int = 50 ,
907+ ) -> list [DeliveryEvent ]:
908+ """Get the delivery event log for messages.
909+
910+ Args:
911+ message_id: Filter events for a specific message.
912+ inbox_id: Filter events for all messages from an inbox.
913+ domain_id: Filter events for all messages from a domain.
914+ event_type: Filter by type: "sent", "delivered", "bounced",
915+ "complained", or "failed".
916+ limit: Max results (default 50).
917+
918+ Returns:
919+ List of DeliveryEvent objects with .event_type, .recipient, .timestamp.
920+ """
921+ params : dict [str , Any ] = {"limit" : limit }
922+ if message_id :
923+ params ["message_id" ] = message_id
924+ if inbox_id :
925+ params ["inbox_id" ] = inbox_id
926+ if domain_id :
927+ params ["domain_id" ] = domain_id
928+ if event_type :
929+ params ["event_type" ] = event_type
930+ data = await self ._http .get ("/v1/delivery/events" , params = params )
931+ if isinstance (data , list ):
932+ return [DeliveryEvent .model_validate (e ) for e in data ]
933+ return [DeliveryEvent .model_validate (e ) for e in (data or [])]
934+
935+
769936class AsyncCommuneClient :
770937 """Async Commune SDK client — email infrastructure for AI agents using asyncio.
771938
@@ -857,6 +1024,9 @@ def __init__(
8571024 self .threads = _AsyncThreads (self ._http )
8581025 self .messages = _AsyncMessages (self ._http )
8591026 self .attachments = _AsyncAttachments (self ._http )
1027+ self .search = _AsyncSearch (self ._http )
1028+ self .sms = _AsyncSms (self ._http )
1029+ self .delivery = _AsyncDelivery (self ._http )
8601030
8611031 async def close (self ) -> None :
8621032 """Close the underlying async HTTP connection pool.
0 commit comments