agentmail 0.4.20 → 0.5.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (89) hide show
  1. package/dist/cjs/BaseClient.js +2 -2
  2. package/dist/cjs/api/resources/events/types/EventType.d.ts +1 -0
  3. package/dist/cjs/api/resources/events/types/EventType.js +1 -0
  4. package/dist/cjs/api/resources/events/types/MessageReceivedEvent.d.ts +4 -1
  5. package/dist/cjs/api/resources/events/types/MessageReceivedEventType.d.ts +7 -0
  6. package/dist/cjs/api/resources/events/types/MessageReceivedEventType.js +10 -0
  7. package/dist/cjs/api/resources/events/types/index.d.ts +1 -2
  8. package/dist/cjs/api/resources/events/types/index.js +1 -2
  9. package/dist/cjs/api/resources/inboxes/resources/messages/client/Client.js +2 -1
  10. package/dist/cjs/api/resources/inboxes/resources/messages/client/requests/ListMessagesRequest.d.ts +1 -0
  11. package/dist/cjs/api/resources/inboxes/resources/threads/client/Client.js +2 -1
  12. package/dist/cjs/api/resources/inboxes/resources/threads/client/requests/ListThreadsRequest.d.ts +1 -0
  13. package/dist/cjs/api/resources/pods/resources/threads/client/Client.js +2 -1
  14. package/dist/cjs/api/resources/pods/resources/threads/client/requests/ListThreadsRequest.d.ts +1 -0
  15. package/dist/cjs/api/resources/threads/client/Client.js +2 -1
  16. package/dist/cjs/api/resources/threads/client/requests/ListThreadsRequest.d.ts +1 -0
  17. package/dist/cjs/api/resources/websockets/client/Socket.d.ts +1 -1
  18. package/dist/cjs/api/types/IncludeUnauthenticated.d.ts +4 -0
  19. package/dist/cjs/api/types/index.d.ts +1 -0
  20. package/dist/cjs/api/types/index.js +1 -0
  21. package/dist/cjs/serialization/resources/events/types/EventType.d.ts +1 -1
  22. package/dist/cjs/serialization/resources/events/types/EventType.js +1 -0
  23. package/dist/cjs/serialization/resources/events/types/MessageReceivedEvent.d.ts +2 -1
  24. package/dist/cjs/serialization/resources/events/types/MessageReceivedEvent.js +2 -1
  25. package/dist/cjs/serialization/resources/events/types/MessageReceivedEventType.d.ts +7 -0
  26. package/dist/cjs/serialization/resources/events/types/{MessageReceivedSpamEvent.js → MessageReceivedEventType.js} +7 -11
  27. package/dist/cjs/serialization/resources/events/types/index.d.ts +1 -2
  28. package/dist/cjs/serialization/resources/events/types/index.js +1 -2
  29. package/dist/cjs/serialization/resources/websockets/client/socket/WebsocketsSocketResponse.d.ts +2 -4
  30. package/dist/cjs/serialization/resources/websockets/client/socket/WebsocketsSocketResponse.js +0 -4
  31. package/dist/cjs/serialization/types/IncludeUnauthenticated.d.ts +7 -0
  32. package/dist/cjs/serialization/{resources/events/types/MessageReceivedBlockedEvent.js → types/IncludeUnauthenticated.js} +3 -12
  33. package/dist/cjs/serialization/types/index.d.ts +1 -0
  34. package/dist/cjs/serialization/types/index.js +1 -0
  35. package/dist/cjs/version.d.ts +1 -1
  36. package/dist/cjs/version.js +1 -1
  37. package/dist/esm/BaseClient.mjs +2 -2
  38. package/dist/esm/api/resources/events/types/EventType.d.mts +1 -0
  39. package/dist/esm/api/resources/events/types/EventType.mjs +1 -0
  40. package/dist/esm/api/resources/events/types/MessageReceivedEvent.d.mts +4 -1
  41. package/dist/esm/api/resources/events/types/MessageReceivedEventType.d.mts +7 -0
  42. package/dist/esm/api/resources/events/types/MessageReceivedEventType.mjs +7 -0
  43. package/dist/esm/api/resources/events/types/index.d.mts +1 -2
  44. package/dist/esm/api/resources/events/types/index.mjs +1 -2
  45. package/dist/esm/api/resources/inboxes/resources/messages/client/Client.mjs +2 -1
  46. package/dist/esm/api/resources/inboxes/resources/messages/client/requests/ListMessagesRequest.d.mts +1 -0
  47. package/dist/esm/api/resources/inboxes/resources/threads/client/Client.mjs +2 -1
  48. package/dist/esm/api/resources/inboxes/resources/threads/client/requests/ListThreadsRequest.d.mts +1 -0
  49. package/dist/esm/api/resources/pods/resources/threads/client/Client.mjs +2 -1
  50. package/dist/esm/api/resources/pods/resources/threads/client/requests/ListThreadsRequest.d.mts +1 -0
  51. package/dist/esm/api/resources/threads/client/Client.mjs +2 -1
  52. package/dist/esm/api/resources/threads/client/requests/ListThreadsRequest.d.mts +1 -0
  53. package/dist/esm/api/resources/websockets/client/Socket.d.mts +1 -1
  54. package/dist/esm/api/types/IncludeUnauthenticated.d.mts +4 -0
  55. package/dist/esm/api/types/index.d.mts +1 -0
  56. package/dist/esm/api/types/index.mjs +1 -0
  57. package/dist/esm/serialization/resources/events/types/EventType.d.mts +1 -1
  58. package/dist/esm/serialization/resources/events/types/EventType.mjs +1 -0
  59. package/dist/esm/serialization/resources/events/types/MessageReceivedEvent.d.mts +2 -1
  60. package/dist/esm/serialization/resources/events/types/MessageReceivedEvent.mjs +2 -1
  61. package/dist/esm/serialization/resources/events/types/MessageReceivedEventType.d.mts +7 -0
  62. package/dist/esm/serialization/resources/events/types/MessageReceivedEventType.mjs +8 -0
  63. package/dist/esm/serialization/resources/events/types/index.d.mts +1 -2
  64. package/dist/esm/serialization/resources/events/types/index.mjs +1 -2
  65. package/dist/esm/serialization/resources/websockets/client/socket/WebsocketsSocketResponse.d.mts +2 -4
  66. package/dist/esm/serialization/resources/websockets/client/socket/WebsocketsSocketResponse.mjs +0 -4
  67. package/dist/esm/serialization/types/IncludeUnauthenticated.d.mts +7 -0
  68. package/dist/esm/serialization/types/IncludeUnauthenticated.mjs +3 -0
  69. package/dist/esm/serialization/types/index.d.mts +1 -0
  70. package/dist/esm/serialization/types/index.mjs +1 -0
  71. package/dist/esm/version.d.mts +1 -1
  72. package/dist/esm/version.mjs +1 -1
  73. package/dist/llms-full.txt +187 -169
  74. package/dist/llms.txt +4 -3
  75. package/package.json +1 -1
  76. package/dist/cjs/api/resources/events/types/MessageReceivedBlockedEvent.d.ts +0 -11
  77. package/dist/cjs/api/resources/events/types/MessageReceivedSpamEvent.d.ts +0 -11
  78. package/dist/cjs/api/resources/events/types/MessageReceivedSpamEvent.js +0 -3
  79. package/dist/cjs/serialization/resources/events/types/MessageReceivedBlockedEvent.d.ts +0 -16
  80. package/dist/cjs/serialization/resources/events/types/MessageReceivedSpamEvent.d.ts +0 -16
  81. package/dist/esm/api/resources/events/types/MessageReceivedBlockedEvent.d.mts +0 -11
  82. package/dist/esm/api/resources/events/types/MessageReceivedSpamEvent.d.mts +0 -11
  83. package/dist/esm/api/resources/events/types/MessageReceivedSpamEvent.mjs +0 -2
  84. package/dist/esm/serialization/resources/events/types/MessageReceivedBlockedEvent.d.mts +0 -16
  85. package/dist/esm/serialization/resources/events/types/MessageReceivedBlockedEvent.mjs +0 -12
  86. package/dist/esm/serialization/resources/events/types/MessageReceivedSpamEvent.d.mts +0 -16
  87. package/dist/esm/serialization/resources/events/types/MessageReceivedSpamEvent.mjs +0 -12
  88. /package/dist/cjs/api/{resources/events/types/MessageReceivedBlockedEvent.js → types/IncludeUnauthenticated.js} +0 -0
  89. /package/dist/esm/api/{resources/events/types/MessageReceivedBlockedEvent.mjs → types/IncludeUnauthenticated.mjs} +0 -0
@@ -1,7 +1,7 @@
1
- > For clean Markdown content of this page, append .md to this URL. For the complete documentation index, see https://docs.agentmail.to/llms.txt. For full content including API reference and SDK examples, see https://docs.agentmail.to/llms-full.txt.
2
-
3
1
  # AgentMail | Documentation
4
2
 
3
+ > AgentMail is an email API built for AI agents. Create inboxes, send and receive messages, manage threads, and handle webhooks programmatically.
4
+
5
5
  # Welcome
6
6
 
7
7
  > Your starting point for building with the AgentMail API.
@@ -1242,7 +1242,7 @@ When processing incoming messages, always treat `html` as the primary content so
1242
1242
  ## Receiving `Messages`
1243
1243
 
1244
1244
  <Warning>
1245
- **Inbound emails require the sender's domain to have SPF or DKIM configured.** To reduce spoofing and phishing, AgentMail drops inbound emails when the sender's domain has neither SPF nor DKIM set up. If a sender reports that their email never arrived, this is the most likely cause. Once they configure SPF/DKIM on their domain, their emails will start landing in your inbox again. See [Why are my emails not showing up?](/knowledge-base/inbound-emails-missing) for details.
1245
+ **Inbound emails are checked for SPF, DKIM, and DMARC authentication.** AgentMail drops emails when authentication headers are present and explicitly fail. When authentication headers are missing, AgentMail still processes the email and labels it `unauthenticated`. Subscribe to the `message.received.unauthenticated` event to monitor these messages. See [Why are my emails not showing up?](/knowledge-base/inbound-emails-missing) for details.
1246
1246
  </Warning>
1247
1247
 
1248
1248
  While you can periodically list `Messages` to check for new emails, the most efficient way to handle incoming `Messages` for your agents is with `Webhooks`. By configuring a `Webhook` endpoint, AgentMail can notify your application/agent in real-time as soon as a new `Message` arrives, so you can take action on them.
@@ -6435,13 +6435,14 @@ This event-driven approach is more efficient and allows you to build fast, respo
6435
6435
 
6436
6436
  ## Available Events
6437
6437
 
6438
- AgentMail supports nine webhook event types. When creating a webhook, you can subscribe to specific events or receive all of them. See [Webhook Events](/events) for full payload details.
6438
+ AgentMail supports ten webhook event types. When creating a webhook, you can subscribe to specific events or receive all standard events. See [Webhook Events](/events) for full payload details.
6439
6439
 
6440
6440
  **Message events:**
6441
6441
 
6442
6442
  * **`message.received`** — New email received and processed in one of your inboxes
6443
6443
  * **`message.received.spam`** — A message was received and classified as spam (requires `label_spam_read` permission)
6444
6444
  * **`message.received.blocked`** — A message was received and matched a block list entry (requires `label_blocked_read` permission)
6445
+ * **`message.received.unauthenticated`** — A message was received without authentication headers, so AgentMail could not verify whether it was authenticated
6445
6446
  * **`message.sent`** — Message successfully sent from your inbox
6446
6447
  * **`message.delivered`** — Delivery confirmed by the recipient's mail server
6447
6448
  * **`message.bounced`** — Message failed to deliver and bounced back
@@ -6453,7 +6454,7 @@ AgentMail supports nine webhook event types. When creating a webhook, you can su
6453
6454
  * **`domain.verified`** — Custom domain successfully verified
6454
6455
 
6455
6456
  <Callout intent="info">
6456
- Spam and blocked events are excluded by default. To receive them, explicitly include `message.received.spam` or `message.received.blocked` in the `event_types` list when creating a webhook. Messages with these labels are no longer sent as `message.received`.
6457
+ Spam, blocked, and unauthenticated events are excluded by default. To receive them, explicitly include `message.received.spam`, `message.received.blocked`, or `message.received.unauthenticated` in the `event_types` list when creating a webhook. These messages are no longer sent as `message.received`.
6457
6458
  </Callout>
6458
6459
 
6459
6460
  ## The Webhook Workflow
@@ -6492,7 +6493,7 @@ The process is straightforward:
6492
6493
  ```
6493
6494
  </CodeBlocks>
6494
6495
 
6495
- Specify which events to receive; omit `event_types` to subscribe to all standard event types. Spam and blocked events must always be explicitly included.
6496
+ Specify which events to receive; omit `event_types` to subscribe to all standard event types. Spam, blocked, and unauthenticated events must always be explicitly included.
6496
6497
  </Step>
6497
6498
 
6498
6499
  <Step title="3. AgentMail Sends Events">
@@ -6613,9 +6614,9 @@ Copy one of the blocks below into Cursor or Claude for complete Webhooks API kno
6613
6614
  - webhooks.update(webhook_id, add_inbox_ids?, remove_inbox_ids?, add_pod_ids?, remove_pod_ids?)
6614
6615
  - webhooks.delete(webhook_id)
6615
6616
 
6616
- Events: message.received, message.received.spam, message.received.blocked, message.sent, message.delivered, message.bounced, message.complained, message.rejected, domain.verified
6617
+ Events: message.received, message.received.spam, message.received.blocked, message.received.unauthenticated, message.sent, message.delivered, message.bounced, message.complained, message.rejected, domain.verified
6617
6618
  Payload: event_type, event_id, plus message/send/delivery/bounce/complaint/reject/domain. Verify with Svix (webhook.secret).
6618
- Note: message.received.spam and message.received.blocked are excluded by default. To receive them, explicitly include them in event_types and ensure the API key has label_spam_read / label_blocked_read permissions.
6619
+ Note: message.received.spam, message.received.blocked, and message.received.unauthenticated are excluded by default. To receive them, explicitly include them in event_types and ensure the API key has label_spam_read / label_blocked_read permissions for spam and blocked events.
6619
6620
  """
6620
6621
  import os
6621
6622
  from dotenv import load_dotenv
@@ -6642,8 +6643,8 @@ Copy one of the blocks below into Cursor or Claude for complete Webhooks API kno
6642
6643
  * - webhooks.update(webhookId, { addInboxIds?, removeInboxIds?, addPodIds?, removePodIds? })
6643
6644
  * - webhooks.delete(webhookId)
6644
6645
  *
6645
- * Events: message.received, message.received.spam, message.received.blocked, message.sent, message.delivered, message.bounced, message.complained, message.rejected, domain.verified
6646
- * Note: message.received.spam and message.received.blocked are excluded by default. To receive them, explicitly include them in eventTypes and ensure the API key has label_spam_read / label_blocked_read permissions.
6646
+ * Events: message.received, message.received.spam, message.received.blocked, message.received.unauthenticated, message.sent, message.delivered, message.bounced, message.complained, message.rejected, domain.verified
6647
+ * Note: message.received.spam, message.received.blocked, and message.received.unauthenticated are excluded by default. To receive them, explicitly include them in eventTypes and ensure the API key has label_spam_read / label_blocked_read permissions for spam and blocked events.
6647
6648
  * Verify with Svix using webhook.secret. Use express.raw() for body—signature needs raw payload.
6648
6649
  */
6649
6650
  import { AgentMailClient } from "agentmail";
@@ -6704,7 +6705,7 @@ All webhook payloads follow the same basic structure:
6704
6705
 
6705
6706
  ## Parsing events with SDKs
6706
6707
 
6707
- The AgentMail SDKs export typed classes for each webhook event, so you can parse raw payloads into fully typed objects.
6708
+ The AgentMail SDKs export typed classes for each webhook payload shape, so you can parse raw payloads into fully typed objects.
6708
6709
 
6709
6710
  <CodeBlocks>
6710
6711
  ```typescript title="TypeScript"
@@ -6712,8 +6713,14 @@ The AgentMail SDKs export typed classes for each webhook event, so you can parse
6712
6713
 
6713
6714
  async function handleWebhook(payload: Record<string, unknown>) {
6714
6715
  const eventType = payload.event_type;
6715
-
6716
- if (eventType === "message.received") {
6716
+ const receivedEventTypes = [
6717
+ "message.received",
6718
+ "message.received.spam",
6719
+ "message.received.blocked",
6720
+ "message.received.unauthenticated",
6721
+ ];
6722
+
6723
+ if (receivedEventTypes.includes(String(eventType))) {
6717
6724
  const event = await serialization.events.MessageReceivedEvent.parse(payload);
6718
6725
  // access typed fields
6719
6726
  console.log(event.message.subject);
@@ -6733,12 +6740,6 @@ The AgentMail SDKs export typed classes for each webhook event, so you can parse
6733
6740
  } else if (eventType === "message.rejected") {
6734
6741
  const event = await serialization.events.MessageRejectedEvent.parse(payload);
6735
6742
  console.log(event.reject.reason);
6736
- } else if (eventType === "message.received.spam") {
6737
- const event = await serialization.events.MessageReceivedSpamEvent.parse(payload);
6738
- console.log(event.message.subject);
6739
- } else if (eventType === "message.received.blocked") {
6740
- const event = await serialization.events.MessageReceivedBlockedEvent.parse(payload);
6741
- console.log(event.message.subject);
6742
6743
  } else if (eventType === "domain.verified") {
6743
6744
  const event = await serialization.events.DomainVerifiedEvent.parse(payload);
6744
6745
  console.log(event.domain.status);
@@ -6749,8 +6750,6 @@ The AgentMail SDKs export typed classes for each webhook event, so you can parse
6749
6750
  ```python title="Python"
6750
6751
  from agentmail import (
6751
6752
  MessageReceivedEvent,
6752
- MessageReceivedSpamEvent,
6753
- MessageReceivedBlockedEvent,
6754
6753
  MessageSentEvent,
6755
6754
  MessageBouncedEvent,
6756
6755
  MessageDeliveredEvent,
@@ -6761,8 +6760,14 @@ The AgentMail SDKs export typed classes for each webhook event, so you can parse
6761
6760
 
6762
6761
  def handle_webhook(payload: dict):
6763
6762
  event_type = payload.get("event_type")
6763
+ received_event_types = {
6764
+ "message.received",
6765
+ "message.received.spam",
6766
+ "message.received.blocked",
6767
+ "message.received.unauthenticated",
6768
+ }
6764
6769
 
6765
- if event_type == "message.received":
6770
+ if event_type in received_event_types:
6766
6771
  event = MessageReceivedEvent(**payload)
6767
6772
  # access typed fields
6768
6773
  print(event.message.subject)
@@ -6782,12 +6787,6 @@ The AgentMail SDKs export typed classes for each webhook event, so you can parse
6782
6787
  elif event_type == "message.rejected":
6783
6788
  event = MessageRejectedEvent(**payload)
6784
6789
  print(event.reject.reason)
6785
- elif event_type == "message.received.spam":
6786
- event = MessageReceivedSpamEvent(**payload)
6787
- print(event.message.subject)
6788
- elif event_type == "message.received.blocked":
6789
- event = MessageReceivedBlockedEvent(**payload)
6790
- print(event.message.subject)
6791
6790
  elif event_type == "domain.verified":
6792
6791
  event = DomainVerifiedEvent(**payload)
6793
6792
  print(event.domain.status)
@@ -6803,28 +6802,27 @@ Copy one of the blocks below into Cursor or Claude for webhook event parsing in
6803
6802
  """
6804
6803
  AgentMail Webhook Events — copy into Cursor/Claude.
6805
6804
 
6806
- Parse typed events: MessageReceivedEvent (message.received), MessageReceivedSpamEvent (message.received.spam), MessageReceivedBlockedEvent (message.received.blocked),
6805
+ Parse typed events: MessageReceivedEvent (message.received, message.received.spam, message.received.blocked, message.received.unauthenticated),
6807
6806
  MessageSentEvent (send), MessageBouncedEvent (bounce), MessageDeliveredEvent (delivery), MessageComplainedEvent (complaint),
6808
6807
  MessageRejectedEvent (reject), DomainVerifiedEvent (domain).
6809
6808
  Only message.received* includes full message+thread; others have send/delivery/bounce/complaint/reject/domain.
6810
6809
  Note: message.received.spam and message.received.blocked require label_spam_read / label_blocked_read permissions.
6810
+ message.received.unauthenticated must be explicitly included in event_types.
6811
6811
  """
6812
6812
  from agentmail import (
6813
- MessageReceivedEvent, MessageReceivedSpamEvent, MessageReceivedBlockedEvent,
6813
+ MessageReceivedEvent,
6814
6814
  MessageSentEvent, MessageBouncedEvent,
6815
6815
  MessageDeliveredEvent, MessageComplainedEvent, MessageRejectedEvent, DomainVerifiedEvent,
6816
6816
  )
6817
6817
 
6818
6818
  def handle(payload: dict):
6819
6819
  t = payload.get("event_type")
6820
- if t == "message.received": e = MessageReceivedEvent(**payload); print(e.message.subject, e.thread.message_count)
6820
+ if t in {"message.received", "message.received.spam", "message.received.blocked", "message.received.unauthenticated"}: e = MessageReceivedEvent(**payload); print(e.message.subject, e.thread.message_count)
6821
6821
  elif t == "message.sent": e = MessageSentEvent(**payload); print(e.send.recipients)
6822
6822
  elif t == "message.bounced": e = MessageBouncedEvent(**payload); print(e.bounce.type)
6823
6823
  elif t == "message.delivered": e = MessageDeliveredEvent(**payload)
6824
6824
  elif t == "message.complained": e = MessageComplainedEvent(**payload)
6825
6825
  elif t == "message.rejected": e = MessageRejectedEvent(**payload); print(e.reject.reason)
6826
- elif t == "message.received.spam": e = MessageReceivedSpamEvent(**payload); print(e.message.subject)
6827
- elif t == "message.received.blocked": e = MessageReceivedBlockedEvent(**payload); print(e.message.subject)
6828
6826
  elif t == "domain.verified": e = DomainVerifiedEvent(**payload); print(e.domain.status)
6829
6827
  ```
6830
6828
 
@@ -6833,15 +6831,22 @@ Copy one of the blocks below into Cursor or Claude for webhook event parsing in
6833
6831
  * AgentMail Webhook Events — copy into Cursor/Claude.
6834
6832
  *
6835
6833
  * Parse with serialization.events.<EventType>.parse(payload).
6836
- * message.received → MessageReceivedEvent, message.received.spam → MessageReceivedSpamEvent, message.received.blocked → MessageReceivedBlockedEvent.
6834
+ * message.received, message.received.spam, message.received.blocked, message.received.unauthenticated MessageReceivedEvent.
6837
6835
  * Only message.received* has message+thread; others have send/delivery/bounce/complaint/reject/domain.
6838
6836
  * message.received.spam and message.received.blocked require label_spam_read / label_blocked_read permissions.
6837
+ * message.received.unauthenticated must be explicitly included in eventTypes.
6839
6838
  */
6840
6839
  import { serialization } from "agentmail";
6841
6840
 
6842
6841
  async function handle(payload: Record<string, unknown>) {
6843
6842
  const t = payload.event_type;
6844
- if (t === "message.received") {
6843
+ const receivedEventTypes = [
6844
+ "message.received",
6845
+ "message.received.spam",
6846
+ "message.received.blocked",
6847
+ "message.received.unauthenticated",
6848
+ ];
6849
+ if (receivedEventTypes.includes(String(t))) {
6845
6850
  const e = await serialization.events.MessageReceivedEvent.parse(payload);
6846
6851
  console.log(e.message.subject, e.thread.messageCount);
6847
6852
  } else if (t === "message.sent") {
@@ -6859,12 +6864,6 @@ Copy one of the blocks below into Cursor or Claude for webhook event parsing in
6859
6864
  } else if (t === "message.rejected") {
6860
6865
  const e = await serialization.events.MessageRejectedEvent.parse(payload);
6861
6866
  console.log(e.reject);
6862
- } else if (t === "message.received.spam") {
6863
- const e = await serialization.events.MessageReceivedSpamEvent.parse(payload);
6864
- console.log(e.message.subject);
6865
- } else if (t === "message.received.blocked") {
6866
- const e = await serialization.events.MessageReceivedBlockedEvent.parse(payload);
6867
- console.log(e.message.subject);
6868
6867
  } else if (t === "domain.verified") {
6869
6868
  const e = await serialization.events.DomainVerifiedEvent.parse(payload);
6870
6869
  console.log(e.domain);
@@ -6881,9 +6880,10 @@ Copy one of the blocks below into Cursor or Claude for webhook event parsing in
6881
6880
  * **Example use-case:** Kick off a internal workflow when a customer complaint email hits the support inbox
6882
6881
 
6883
6882
  <Callout>
6884
- Something here to notice is message.received is the only webhook event that
6885
- includes both the metadata on the `Thread` and the `Message` in the payload.
6886
- Other event types send only metadata on the event. Let us know if you need
6883
+ Something here to notice is `message.received` and its filtered variants are
6884
+ the only webhook events that include both the metadata on the `Thread` and the
6885
+ `Message` in the payload. Other event types send only metadata on the event.
6886
+ Let us know if you need
6887
6887
  metadata on other event types by emailing `support@agentmail.cc`
6888
6888
  </Callout>
6889
6889
 
@@ -7035,6 +7035,52 @@ Copy one of the blocks below into Cursor or Claude for webhook event parsing in
7035
7035
  ```
7036
7036
  </CodeGroup>
7037
7037
 
7038
+ ### `message.received.unauthenticated`
7039
+
7040
+ * **Description:** Triggered when a new email is received without authentication headers, so AgentMail cannot verify whether it is authenticated. These messages are processed and labeled `unauthenticated` because legitimate senders may omit the headers; messages with authentication headers that explicitly fail are dropped.
7041
+ * **Example use-case:** Monitor senders whose messages are missing authentication headers so you can decide whether to trust them or ask them to improve their email setup.
7042
+
7043
+ <CodeGroup>
7044
+ ```json
7045
+ {
7046
+ "type": "event",
7047
+ "event_type": "message.received.unauthenticated",
7048
+ "event_id": "evt_unauth789",
7049
+ "message": {
7050
+ "inbox_id": "inbox_456def",
7051
+ "thread_id": "thd_789ghi",
7052
+ "message_id": "<unauth789@example.com>",
7053
+ "labels": ["unauthenticated"],
7054
+ "timestamp": "2023-10-27T10:00:00Z",
7055
+ "from": "sender@example.com",
7056
+ "to": ["agent@agentmail.to"],
7057
+ "subject": "Unauthenticated message",
7058
+ "preview": "This message is missing authentication headers...",
7059
+ "text": "Full unauthenticated message body.",
7060
+ "html": "<html>...</html>",
7061
+ "size": 512,
7062
+ "updated_at": "2023-10-27T10:00:00Z",
7063
+ "created_at": "2023-10-27T10:00:00Z"
7064
+ },
7065
+ "thread": {
7066
+ "inbox_id": "inbox_456def",
7067
+ "thread_id": "thd_789ghi",
7068
+ "labels": ["unauthenticated"],
7069
+ "timestamp": "2023-10-27T10:00:00Z",
7070
+ "senders": ["sender@example.com"],
7071
+ "recipients": ["agent@agentmail.to"],
7072
+ "subject": "Unauthenticated message",
7073
+ "preview": "This message is missing authentication headers...",
7074
+ "last_message_id": "<unauth789@example.com>",
7075
+ "message_count": 1,
7076
+ "size": 512,
7077
+ "updated_at": "2023-10-27T10:00:00Z",
7078
+ "created_at": "2023-10-27T10:00:00Z"
7079
+ }
7080
+ }
7081
+ ```
7082
+ </CodeGroup>
7083
+
7038
7084
  ### `message.sent`
7039
7085
 
7040
7086
  * **Description:** Triggered when a message is successfully sent from one of your `Inboxes`.
@@ -7223,7 +7269,7 @@ When creating a webhook, you can specify which events to subscribe to. This allo
7223
7269
  For example, if you only need to trigger workflows on incoming messages, you can subscribe to just `message.received`. If you're building a delivery tracking system, you might subscribe to `message.sent`, `message.delivered`, and `message.bounced`.
7224
7270
 
7225
7271
  <Callout intent="info">
7226
- By default, spam and blocked events are **not** delivered. To receive them, you must explicitly include `message.received.spam` or `message.received.blocked` in the `event_types` list when creating your webhook. The API key must also have the corresponding `label_spam_read` or `label_blocked_read` permission.
7272
+ By default, spam, blocked, and unauthenticated events are **not** delivered. To receive them, you must explicitly include `message.received.spam`, `message.received.blocked`, or `message.received.unauthenticated` in the `event_types` list when creating your webhook. The API key must also have the corresponding `label_spam_read` or `label_blocked_read` permission for spam and blocked events.
7227
7273
  </Callout>
7228
7274
 
7229
7275
  If you have any specific webhook notifications you would like, please ping us in the `#feature-requests` channel in the [Discord](https://discord.gg/hTYatWYWBc)
@@ -8214,10 +8260,15 @@ Subscribe(
8214
8260
  event_types=["message.received", "message.sent"]
8215
8261
  )
8216
8262
 
8217
- # Subscribe to spam and blocked events (requires label_spam_read / label_blocked_read permissions)
8263
+ # Subscribe to filtered inbound events
8218
8264
  Subscribe(
8219
8265
  inbox_ids=["agent@agentmail.to"],
8220
- event_types=["message.received", "message.received.spam", "message.received.blocked"]
8266
+ event_types=[
8267
+ "message.received",
8268
+ "message.received.spam",
8269
+ "message.received.blocked",
8270
+ "message.received.unauthenticated",
8271
+ ]
8221
8272
  )
8222
8273
  ```
8223
8274
 
@@ -8243,16 +8294,21 @@ socket.sendSubscribe({
8243
8294
  eventTypes: ["message.received", "message.sent"],
8244
8295
  });
8245
8296
 
8246
- // Subscribe to spam and blocked events (requires label_spam_read / label_blocked_read permissions)
8297
+ // Subscribe to filtered inbound events
8247
8298
  socket.sendSubscribe({
8248
8299
  type: "subscribe",
8249
8300
  inboxIds: ["agent@agentmail.to"],
8250
- eventTypes: ["message.received", "message.received.spam", "message.received.blocked"],
8301
+ eventTypes: [
8302
+ "message.received",
8303
+ "message.received.spam",
8304
+ "message.received.blocked",
8305
+ "message.received.unauthenticated",
8306
+ ],
8251
8307
  });
8252
8308
  ```
8253
8309
 
8254
8310
  <Callout intent="info">
8255
- By default (when no `event_types` are specified), spam and blocked events are excluded from the subscription. To receive spam events, explicitly include `message.received.spam` in `event_types` and ensure the API key has the `label_spam_read` permission. The same applies to `message.received.blocked` with the `label_blocked_read` permission.
8311
+ By default (when no `event_types` are specified), spam, blocked, and unauthenticated events are excluded from the subscription. To receive them, explicitly include `message.received.spam`, `message.received.blocked`, or `message.received.unauthenticated` in `event_types`. Spam and blocked events also require the `label_spam_read` or `label_blocked_read` permission.
8256
8312
  </Callout>
8257
8313
 
8258
8314
  ***
@@ -8267,16 +8323,14 @@ socket.sendSubscribe({
8267
8323
 
8268
8324
  ### Message Events
8269
8325
 
8270
- | Event | Python | TypeScript | Description |
8271
- | -------------------------- | ----------------------------- | --------------------------------------- | ------------------------------------------ |
8272
- | `message_received` | `MessageReceivedEvent` | `AgentMail.MessageReceivedEvent` | New email received |
8273
- | `message_received_spam` | `MessageReceivedSpamEvent` | `AgentMail.MessageReceivedSpamEvent` | Email received, classified as spam |
8274
- | `message_received_blocked` | `MessageReceivedBlockedEvent` | `AgentMail.MessageReceivedBlockedEvent` | Email received, matched a block list entry |
8275
- | `message_sent` | `MessageSentEvent` | `AgentMail.MessageSentEvent` | Email was sent |
8276
- | `message_delivered` | `MessageDeliveredEvent` | `AgentMail.MessageDeliveredEvent` | Email was delivered |
8277
- | `message_bounced` | `MessageBouncedEvent` | `AgentMail.MessageBouncedEvent` | Email bounced |
8278
- | `message_complained` | `MessageComplainedEvent` | `AgentMail.MessageComplainedEvent` | Email marked as spam |
8279
- | `message_rejected` | `MessageRejectedEvent` | `AgentMail.MessageRejectedEvent` | Email was rejected |
8326
+ | Event | Python | TypeScript | Description |
8327
+ | -------------------- | ------------------------ | ---------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------- |
8328
+ | `message_received` | `MessageReceivedEvent` | `AgentMail.MessageReceivedEvent` | Email received. Check `event_type` for `message.received`, `message.received.spam`, `message.received.blocked`, or `message.received.unauthenticated` |
8329
+ | `message_sent` | `MessageSentEvent` | `AgentMail.MessageSentEvent` | Email was sent |
8330
+ | `message_delivered` | `MessageDeliveredEvent` | `AgentMail.MessageDeliveredEvent` | Email was delivered |
8331
+ | `message_bounced` | `MessageBouncedEvent` | `AgentMail.MessageBouncedEvent` | Email bounced |
8332
+ | `message_complained` | `MessageComplainedEvent` | `AgentMail.MessageComplainedEvent` | Email marked as spam |
8333
+ | `message_rejected` | `MessageRejectedEvent` | `AgentMail.MessageRejectedEvent` | Email was rejected |
8280
8334
 
8281
8335
  ### Domain Events
8282
8336
 
@@ -8388,8 +8442,8 @@ Copy one of the blocks below into Cursor or Claude for WebSockets in one shot.
8388
8442
  Sync: with client.websockets.connect() as socket: socket.send_subscribe(Subscribe(inbox_ids=[...])); for event in socket: ...
8389
8443
  Async: async with client.websockets.connect() as socket: await socket.send_subscribe(...); async for event in socket: ...
8390
8444
  Subscribe(inbox_ids=[...], pod_ids=[...], event_types=[...])
8391
- Event types: Subscribed, MessageReceivedEvent, MessageReceivedSpamEvent, MessageReceivedBlockedEvent, MessageSentEvent, MessageDeliveredEvent, MessageBouncedEvent, MessageComplainedEvent, MessageRejectedEvent, DomainVerifiedEvent
8392
- Spam/blocked events require explicit opt-in via event_types and label_spam_read / label_blocked_read permissions.
8445
+ Event types: Subscribed, MessageReceivedEvent, MessageSentEvent, MessageDeliveredEvent, MessageBouncedEvent, MessageComplainedEvent, MessageRejectedEvent, DomainVerifiedEvent
8446
+ Spam, blocked, and unauthenticated events require explicit opt-in via event_types. Spam and blocked events require label_spam_read / label_blocked_read permissions.
8393
8447
  """
8394
8448
  from agentmail import AgentMail, Subscribe, Subscribed, MessageReceivedEvent
8395
8449
 
@@ -24164,9 +24218,15 @@ components:
24164
24218
  type_inbox-events:InboxEventType:
24165
24219
  type: string
24166
24220
  enum:
24167
- - label_added
24168
- - label_removed
24169
- description: Type of inbox event.
24221
+ - label.added
24222
+ - label.removed
24223
+ description: |-
24224
+ Type of inbox event. Wire format is dot.case to match the
24225
+ convention used by webhook events (`message.received`,
24226
+ `domain.verified`, etc. in events.yml). Pre-2026-04 these were
24227
+ `label_added`/`label_removed` (snake_case). The Fern enum's `name`
24228
+ field stays uppercase-snake (Fern convention); only the wire
24229
+ `value` changed.
24170
24230
  title: InboxEventType
24171
24231
  type_inbox-events:InboxEvent:
24172
24232
  type: object
@@ -28571,6 +28631,7 @@ components:
28571
28631
  - message.received
28572
28632
  - message.received.spam
28573
28633
  - message.received.blocked
28634
+ - message.received.unauthenticated
28574
28635
  - message.sent
28575
28636
  - message.delivered
28576
28637
  - message.bounced
@@ -28865,6 +28926,7 @@ components:
28865
28926
  - message.received
28866
28927
  - message.received.spam
28867
28928
  - message.received.blocked
28929
+ - message.received.unauthenticated
28868
28930
  - message.sent
28869
28931
  - message.delivered
28870
28932
  - message.bounced
@@ -29163,6 +29225,7 @@ components:
29163
29225
  - message.received
29164
29226
  - message.received.spam
29165
29227
  - message.received.blocked
29228
+ - message.received.unauthenticated
29166
29229
  - message.sent
29167
29230
  - message.delivered
29168
29231
  - message.bounced
@@ -29559,6 +29622,7 @@ components:
29559
29622
  - message.received
29560
29623
  - message.received.spam
29561
29624
  - message.received.blocked
29625
+ - message.received.unauthenticated
29562
29626
  - message.sent
29563
29627
  - message.delivered
29564
29628
  - message.bounced
@@ -30073,6 +30137,14 @@ components:
30073
30137
  format: date-time
30074
30138
  description: Timestamp of webhook message.
30075
30139
  title: SvixTimestamp
30140
+ type_events:MessageReceivedEventType:
30141
+ type: string
30142
+ enum:
30143
+ - message.received
30144
+ - message.received.spam
30145
+ - message.received.blocked
30146
+ - message.received.unauthenticated
30147
+ title: MessageReceivedEventType
30076
30148
  type_events:EventId:
30077
30149
  type: string
30078
30150
  description: ID of event.
@@ -30425,9 +30497,7 @@ components:
30425
30497
  enum:
30426
30498
  - event
30427
30499
  event_type:
30428
- type: string
30429
- enum:
30430
- - message.received
30500
+ $ref: '#/components/schemas/type_events:MessageReceivedEventType'
30431
30501
  event_id:
30432
30502
  $ref: '#/components/schemas/type_events:EventId'
30433
30503
  message:
@@ -30440,6 +30510,10 @@ components:
30440
30510
  - event_id
30441
30511
  - message
30442
30512
  - thread
30513
+ description: >-
30514
+ A message was received. Spam, blocked, and unauthenticated
30515
+ received-message events use the same payload shape with different
30516
+ `event_type` values.
30443
30517
  title: MessageReceivedEvent
30444
30518
 
30445
30519
  ```
@@ -31328,23 +31402,19 @@ channels:
31328
31402
  - $ref: >-
31329
31403
  #/components/messages/subpackage_websockets.websockets-server-1-message_received
31330
31404
  - $ref: >-
31331
- #/components/messages/subpackage_websockets.websockets-server-2-message_received_spam
31405
+ #/components/messages/subpackage_websockets.websockets-server-2-message_sent
31332
31406
  - $ref: >-
31333
- #/components/messages/subpackage_websockets.websockets-server-3-message_received_blocked
31407
+ #/components/messages/subpackage_websockets.websockets-server-3-message_delivered
31334
31408
  - $ref: >-
31335
- #/components/messages/subpackage_websockets.websockets-server-4-message_sent
31409
+ #/components/messages/subpackage_websockets.websockets-server-4-message_bounced
31336
31410
  - $ref: >-
31337
- #/components/messages/subpackage_websockets.websockets-server-5-message_delivered
31411
+ #/components/messages/subpackage_websockets.websockets-server-5-message_complained
31338
31412
  - $ref: >-
31339
- #/components/messages/subpackage_websockets.websockets-server-6-message_bounced
31413
+ #/components/messages/subpackage_websockets.websockets-server-6-message_rejected
31340
31414
  - $ref: >-
31341
- #/components/messages/subpackage_websockets.websockets-server-7-message_complained
31415
+ #/components/messages/subpackage_websockets.websockets-server-7-domain_verified
31342
31416
  - $ref: >-
31343
- #/components/messages/subpackage_websockets.websockets-server-8-message_rejected
31344
- - $ref: >-
31345
- #/components/messages/subpackage_websockets.websockets-server-9-domain_verified
31346
- - $ref: >-
31347
- #/components/messages/subpackage_websockets.websockets-server-10-error
31417
+ #/components/messages/subpackage_websockets.websockets-server-8-error
31348
31418
  subscribe:
31349
31419
  operationId: websockets-subscribe
31350
31420
  summary: Client message
@@ -31376,39 +31446,31 @@ components:
31376
31446
  name: message_received
31377
31447
  payload:
31378
31448
  $ref: '#/components/schemas/type_events:MessageReceivedEvent'
31379
- subpackage_websockets.websockets-server-2-message_received_spam:
31380
- name: message_received_spam
31381
- payload:
31382
- $ref: '#/components/schemas/type_events:MessageReceivedSpamEvent'
31383
- subpackage_websockets.websockets-server-3-message_received_blocked:
31384
- name: message_received_blocked
31385
- payload:
31386
- $ref: '#/components/schemas/type_events:MessageReceivedBlockedEvent'
31387
- subpackage_websockets.websockets-server-4-message_sent:
31449
+ subpackage_websockets.websockets-server-2-message_sent:
31388
31450
  name: message_sent
31389
31451
  payload:
31390
31452
  $ref: '#/components/schemas/type_events:MessageSentEvent'
31391
- subpackage_websockets.websockets-server-5-message_delivered:
31453
+ subpackage_websockets.websockets-server-3-message_delivered:
31392
31454
  name: message_delivered
31393
31455
  payload:
31394
31456
  $ref: '#/components/schemas/type_events:MessageDeliveredEvent'
31395
- subpackage_websockets.websockets-server-6-message_bounced:
31457
+ subpackage_websockets.websockets-server-4-message_bounced:
31396
31458
  name: message_bounced
31397
31459
  payload:
31398
31460
  $ref: '#/components/schemas/type_events:MessageBouncedEvent'
31399
- subpackage_websockets.websockets-server-7-message_complained:
31461
+ subpackage_websockets.websockets-server-5-message_complained:
31400
31462
  name: message_complained
31401
31463
  payload:
31402
31464
  $ref: '#/components/schemas/type_events:MessageComplainedEvent'
31403
- subpackage_websockets.websockets-server-8-message_rejected:
31465
+ subpackage_websockets.websockets-server-6-message_rejected:
31404
31466
  name: message_rejected
31405
31467
  payload:
31406
31468
  $ref: '#/components/schemas/type_events:MessageRejectedEvent'
31407
- subpackage_websockets.websockets-server-9-domain_verified:
31469
+ subpackage_websockets.websockets-server-7-domain_verified:
31408
31470
  name: domain_verified
31409
31471
  payload:
31410
31472
  $ref: '#/components/schemas/type_events:DomainVerifiedEvent'
31411
- subpackage_websockets.websockets-server-10-error:
31473
+ subpackage_websockets.websockets-server-8-error:
31412
31474
  name: error
31413
31475
  payload:
31414
31476
  $ref: '#/components/schemas/type_websockets:Error'
@@ -31419,6 +31481,7 @@ components:
31419
31481
  - message.received
31420
31482
  - message.received.spam
31421
31483
  - message.received.blocked
31484
+ - message.received.unauthenticated
31422
31485
  - message.sent
31423
31486
  - message.delivered
31424
31487
  - message.bounced
@@ -31460,6 +31523,14 @@ components:
31460
31523
  required:
31461
31524
  - type
31462
31525
  title: Subscribed
31526
+ type_events:MessageReceivedEventType:
31527
+ type: string
31528
+ enum:
31529
+ - message.received
31530
+ - message.received.spam
31531
+ - message.received.blocked
31532
+ - message.received.unauthenticated
31533
+ title: MessageReceivedEventType
31463
31534
  type_events:EventId:
31464
31535
  type: string
31465
31536
  description: ID of event.
@@ -31812,60 +31883,7 @@ components:
31812
31883
  enum:
31813
31884
  - event
31814
31885
  event_type:
31815
- type: string
31816
- enum:
31817
- - message.received
31818
- event_id:
31819
- $ref: '#/components/schemas/type_events:EventId'
31820
- message:
31821
- $ref: '#/components/schemas/type_messages:Message'
31822
- thread:
31823
- $ref: '#/components/schemas/type_threads:ThreadItem'
31824
- required:
31825
- - type
31826
- - event_type
31827
- - event_id
31828
- - message
31829
- - thread
31830
- title: MessageReceivedEvent
31831
- type_events:MessageReceivedSpamEvent:
31832
- type: object
31833
- properties:
31834
- type:
31835
- type: string
31836
- enum:
31837
- - event
31838
- event_type:
31839
- type: string
31840
- enum:
31841
- - message.received.spam
31842
- event_id:
31843
- $ref: '#/components/schemas/type_events:EventId'
31844
- message:
31845
- $ref: '#/components/schemas/type_messages:Message'
31846
- thread:
31847
- $ref: '#/components/schemas/type_threads:ThreadItem'
31848
- required:
31849
- - type
31850
- - event_type
31851
- - event_id
31852
- - message
31853
- - thread
31854
- description: >-
31855
- A message was received and classified as spam. Requires
31856
- `label_spam_read` permission.
31857
- title: MessageReceivedSpamEvent
31858
- type_events:MessageReceivedBlockedEvent:
31859
- type: object
31860
- properties:
31861
- type:
31862
- type: string
31863
- enum:
31864
- - event
31865
- event_type:
31866
- type: string
31867
- enum:
31868
- - message.received.blocked
31886
+ $ref: '#/components/schemas/type_events:MessageReceivedEventType'
31869
31887
  event_id:
31870
31888
  $ref: '#/components/schemas/type_events:EventId'
31871
31889
  message:
@@ -31879,9 +31897,10 @@ components:
31879
31897
  - message
31880
31898
  - thread
31881
31899
  description: >-
31882
- A message was received and matched a block list entry. Requires
31883
- `label_blocked_read` permission.
31884
- title: MessageReceivedBlockedEvent
31900
+ A message was received. Spam, blocked, and unauthenticated
31901
+ received-message events use the same payload shape with different
31902
+ `event_type` values.
31903
+ title: MessageReceivedEvent
31885
31904
  type_events:Timestamp:
31886
31905
  type: string
31887
31906
  format: date-time
@@ -49073,40 +49092,39 @@ For more on maintaining healthy sending metrics, see the [Email Deliverability](
49073
49092
  # Why are my emails not showing up?
49074
49093
 
49075
49094
  <Warning>
49076
- **The sender's domain must have SPF or DKIM configured.** To reduce spoofing and phishing, AgentMail now drops inbound emails when the sender's domain has neither SPF nor DKIM set up. If a sender reports that their email never arrived, this is the most likely cause once they configure SPF/DKIM on their domain, their emails will start landing in your inbox again.
49095
+ **Inbound emails are checked for SPF, DKIM, and DMARC authentication.** AgentMail drops emails when authentication headers are present and explicitly fail. If authentication headers are missing, AgentMail still processes the email and labels it `unauthenticated`.
49077
49096
  </Warning>
49078
49097
 
49079
- AgentMail verifies every inbound email using SPF, DKIM, and DMARC authentication before delivering it to your inbox. Emails that fail any of these checks are dropped to prevent spoofing, phishing, and spam from reaching your agents.
49098
+ AgentMail verifies inbound email using SPF, DKIM, and DMARC authentication before delivering it to your inbox. Emails with authentication headers that explicitly fail are dropped to prevent spoofing, phishing, and spam from reaching your agents.
49080
49099
 
49081
49100
  This is the same standard enforced by Gmail, Outlook, Yahoo, and other major email providers.
49082
49101
 
49083
49102
  ## How inbound authentication works
49084
49103
 
49085
- When an email arrives, AgentMail inspects the SPF, DKIM, and DMARC verdicts provided by the receiving mail server. To be delivered, an email must meet **all** of the following conditions:
49104
+ When an email arrives, AgentMail inspects the SPF, DKIM, and DMARC verdicts provided by the receiving mail server. To be delivered as authenticated, an email must meet **all** of the following conditions:
49086
49105
 
49087
49106
  * Neither SPF nor DKIM returns a `FAIL` verdict
49088
- * At least one of SPF or DKIM is configured (not both `GRAY`)
49089
49107
  * DMARC does not return `FAIL` (unless the sender's DMARC policy is `none`)
49090
49108
  * The message passes virus scanning
49091
49109
 
49092
- If any check fails, the email is dropped.
49110
+ If authentication headers are present and any check fails, the email is dropped. If authentication headers are missing, the email is delivered with the `unauthenticated` label because some legitimate senders do not include those headers.
49093
49111
 
49094
- | Scenario | SPF | DKIM | Result |
49095
- | ------------------------------ | ---- | ---- | --------- |
49096
- | Both pass | PASS | PASS | Delivered |
49097
- | One passes, other unconfigured | PASS | GRAY | Delivered |
49098
- | One passes, other unconfigured | GRAY | PASS | Delivered |
49099
- | Neither configured | GRAY | GRAY | Dropped |
49100
- | Either explicitly fails | FAIL | any | Dropped |
49101
- | Either explicitly fails | any | FAIL | Dropped |
49112
+ | Scenario | SPF | DKIM | Result |
49113
+ | ------------------------------ | ---- | ---- | ------------------------------ |
49114
+ | Both pass | PASS | PASS | Delivered |
49115
+ | One passes, other unconfigured | PASS | GRAY | Delivered |
49116
+ | One passes, other unconfigured | GRAY | PASS | Delivered |
49117
+ | Authentication headers missing | GRAY | GRAY | Delivered as `unauthenticated` |
49118
+ | Either explicitly fails | FAIL | any | Dropped |
49119
+ | Either explicitly fails | any | FAIL | Dropped |
49102
49120
 
49103
49121
  <Warning>
49104
- Emails dropped due to failed authentication are **silently rejected**. The sender's mail server may still report successful delivery because AgentMail's servers did receive the message, but it was rejected during authentication verification. The sender gets no bounce-back or error notification.
49122
+ Emails dropped due to failed authentication are rejected without a bounce-back to the sender. The sender's mail server may still report successful delivery because AgentMail's servers did receive the message, but it was rejected during authentication verification. To monitor emails with missing authentication headers, subscribe to the `message.received.unauthenticated` webhook or WebSocket event.
49105
49123
  </Warning>
49106
49124
 
49107
49125
  ## Why this matters
49108
49126
 
49109
- Without these checks, anyone could send emails claiming to be from any domain. SPF and DKIM authentication proves that the sending server is authorized to send on behalf of the domain in the `From` address, while DMARC ties those results to the domain's published policy. Dropping unauthenticated emails protects your agents from:
49127
+ Without these checks, anyone could send emails claiming to be from any domain. SPF and DKIM authentication proves that the sending server is authorized to send on behalf of the domain in the `From` address, while DMARC ties those results to the domain's published policy. Dropping emails that explicitly fail authentication protects your agents from:
49110
49128
 
49111
49129
  * **Spoofing:** forged sender addresses impersonating trusted contacts
49112
49130
  * **Phishing:** malicious emails designed to trick your agent into taking harmful actions
@@ -49114,7 +49132,7 @@ Without these checks, anyone could send emails claiming to be from any domain. S
49114
49132
 
49115
49133
  ## Diagnosing missing emails
49116
49134
 
49117
- If a sender reports that their email never arrived, check whether their domain has SPF and DKIM records configured:
49135
+ If a sender reports that their email never arrived, check whether their domain's authentication records are configured correctly:
49118
49136
 
49119
49137
  ```bash
49120
49138
  # check for SPF record
@@ -49126,11 +49144,11 @@ For DKIM, use [MXToolbox DKIM Lookup](https://mxtoolbox.com/dkim.aspx) or a simi
49126
49144
 
49127
49145
  You can also use [MXToolbox SPF Lookup](https://mxtoolbox.com/spf.aspx) to verify SPF records online.
49128
49146
 
49129
- If the domain has neither an SPF record nor a DKIM record, that is the cause. Emails from that domain will be dropped by AgentMail and likely by other providers as well.
49147
+ If the sender's authentication headers are missing, AgentMail will still process the email and mark it `unauthenticated`. If those headers are present but fail, the email is dropped by AgentMail and may be rejected by other providers as well.
49130
49148
 
49131
49149
  ## What to tell the sender
49132
49150
 
49133
- The sender needs to configure SPF and/or DKIM on their domain's DNS. This is not something you can fix on the AgentMail side, as the records must be set up by the domain owner.
49151
+ The sender should configure SPF and/or DKIM on their domain's DNS and make sure their mail provider includes authentication headers. This is not something you can fix on the AgentMail side, as the records and headers must be set up by the domain owner or their email provider.
49134
49152
 
49135
49153
  * Add an SPF record to authorize their mail server (e.g., `v=spf1 include:_spf.google.com ~all` for Google Workspace)
49136
49154
  * Enable DKIM signing through their email provider's admin console