Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions changelog.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,19 @@ description: "New features, improvements, and fixes across the Sendmux platform.
rss: true
---

<Update label="28 July 2026" tags={["Bug fixes"]} rss={{ title: "Outbound billing now counts accepted recipient occurrences and newer account setup links replace older ones" }}>
## Bug fixes

Outgoing usage now counts every accepted To, CC, and BCC recipient occurrence. If the same address appears more than once, each accepted occurrence is billed; addresses rejected before acceptance are not charged.

Delivery log summaries show accepted and rejected recipient totals. Log details list each recipient outcome in sending order.

Blind-copy addresses remain part of delivery without being exposed in the delivered message headers.

Repeated account setup requests reuse work already in progress. When a newer setup email is issued, its link replaces the earlier link so only the latest setup link can be used.

</Update>

<Update label="27 July 2026" tags={["Bug fixes"]} rss={{ title: "Automatic sending account disabling now retries incomplete updates" }}>
## Bug fixes

Expand Down
8 changes: 6 additions & 2 deletions guides/billing.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,14 @@ Sendmux charges email usage from the team balance.

| Activity | Rate |
| --- | --- |
| Standard outgoing mail through your own providers | `$0.15 / 1,000 emails` |
| Outgoing mail through the managed Amazon SES route | `$0.25 / 1,000 emails` |
| Standard outgoing mail through your own providers | `$0.15 / 1,000 provider-accepted recipient occurrences` |
| Outgoing mail through the managed Amazon SES route | `$0.25 / 1,000 provider-accepted recipient occurrences` |
| Accepted inbound mail | `$0.15 / 1,000 emails` |

For outgoing mail, each provider-accepted To, CC, or BCC recipient occurrence is billed. Addresses rejected before provider acceptance are not charged.

If the same address appears in more than one recipient field, each provider-accepted occurrence is billed separately.

Inbound usage is counted when a message is accepted into a mailbox. Mail rejected by mailbox allow or block rules is not billed.

Usage transactions can be grouped, so the transaction table may show one **Incoming email usage** line for several received messages in a short period.
Expand Down
228 changes: 226 additions & 2 deletions openapi-app.json
Original file line number Diff line number Diff line change
Expand Up @@ -311,9 +311,168 @@
],
"type": "object"
},
"DeliveryLogDetail": {
"additionalProperties": false,
"properties": {
"accepted_recipient_count": {
"description": "Recipient occurrences accepted by the provider, or null when no recipient snapshot exists",
"maximum": 50,
"minimum": 0,
"type": [
"integer",
"null"
]
},
"attempts": {
"type": "number"
},
"created_at": {
"description": "ISO 8601 creation timestamp",
"type": "string"
},
"from_email": {
"type": [
"string",
"null"
]
},
"id": {
"description": "Log public ID",
"example": "dlog_clxxxxxxxxxxxxxxxxxxxxxxxxx",
"type": "string"
},
"message_id": {
"description": "Email Message-ID header",
"type": [
"string",
"null"
]
},
"provider_id": {
"description": "Provider public ID",
"type": [
"string",
"null"
]
},
"provider_name": {
"description": "Provider display name",
"type": [
"string",
"null"
]
},
"recipient_count": {
"description": "Total recipient occurrences, or null when no recipient snapshot exists",
"maximum": 50,
"minimum": 0,
"type": [
"integer",
"null"
]
},
"recipients": {
"description": "Ordered recipient outcome snapshot for this send, or null when no snapshot exists",
"items": {
"$ref": "#/components/schemas/DeliveryLogRecipient"
},
"maxItems": 50,
"type": [
"array",
"null"
]
},
"rejected_recipient_count": {
"description": "Recipient occurrences rejected by the provider, or null when no recipient snapshot exists",
"maximum": 50,
"minimum": 0,
"type": [
"integer",
"null"
]
},
"sent_at": {
"description": "ISO 8601 timestamp when email was sent",
"type": [
"string",
"null"
]
},
"sent_from_email": {
"description": "The actual from address used for delivery after transformation. NULL if no transformation occurred or for older log entries.",
"type": [
"string",
"null"
]
},
"size_bytes": {
"type": [
"number",
"null"
]
},
"status": {
"enum": [
"pending",
"sent",
"failed",
"rejected"
],
"type": "string"
},
"status_reason": {
"type": [
"string",
"null"
]
},
"subject": {
"type": [
"string",
"null"
]
},
"to_email": {
"type": [
"string",
"null"
]
}
},
"required": [
"id",
"provider_id",
"provider_name",
"message_id",
"from_email",
"sent_from_email",
"to_email",
"subject",
"status",
"status_reason",
"attempts",
"sent_at",
"size_bytes",
"created_at",
"recipient_count",
"accepted_recipient_count",
"rejected_recipient_count",
"recipients"
],
"type": "object"
},
"DeliveryLogItem": {
"additionalProperties": false,
"properties": {
"accepted_recipient_count": {
"description": "Recipient occurrences accepted by the provider, or null when no recipient snapshot exists",
"maximum": 50,
"minimum": 0,
"type": [
"integer",
"null"
]
},
"attempts": {
"type": "number"
},
Expand Down Expand Up @@ -353,6 +512,24 @@
"null"
]
},
"recipient_count": {
"description": "Total recipient occurrences, or null when no recipient snapshot exists",
"maximum": 50,
"minimum": 0,
"type": [
"integer",
"null"
]
},
"rejected_recipient_count": {
"description": "Recipient occurrences rejected by the provider, or null when no recipient snapshot exists",
"maximum": 50,
"minimum": 0,
"type": [
"integer",
"null"
]
},
"sent_at": {
"description": "ISO 8601 timestamp when email was sent",
"type": [
Expand Down Expand Up @@ -415,7 +592,10 @@
"attempts",
"sent_at",
"size_bytes",
"created_at"
"created_at",
"recipient_count",
"accepted_recipient_count",
"rejected_recipient_count"
],
"type": "object"
},
Expand Down Expand Up @@ -456,7 +636,7 @@
{
"properties": {
"data": {
"$ref": "#/components/schemas/DeliveryLogItem"
"$ref": "#/components/schemas/DeliveryLogDetail"
},
"meta": {
"$ref": "#/components/schemas/ResponseMeta"
Expand All @@ -470,6 +650,50 @@
],
"unevaluatedProperties": false
},
"DeliveryLogRecipient": {
"additionalProperties": false,
"properties": {
"email": {
"description": "Recipient email address",
"format": "email",
"type": "string"
},
"reason": {
"description": "Recipient-specific rejection reason when available",
"type": [
"string",
"null"
]
},
"status": {
"description": "Provider acceptance outcome",
"enum": [
"accepted",
"rejected"
],
"type": "string"
},
"type": {
"description": "Recipient header type when known",
"enum": [
"to",
"cc",
"bcc"
],
"type": [
"string",
"null"
]
}
},
"required": [
"email",
"type",
"status",
"reason"
],
"type": "object"
},
"DomainDeletedResponse": {
"allOf": [
{
Expand Down
8 changes: 4 additions & 4 deletions openapi-sending.json
Original file line number Diff line number Diff line change
Expand Up @@ -337,19 +337,19 @@
"type": "array"
},
"bcc": {
"description": "BCC recipients (max 100)",
"description": "BCC recipients (subject to 50 total To, CC, and BCC recipients)",
"items": {
"$ref": "#/components/schemas/Recipient"
},
"maxItems": 100,
"maxItems": 49,
"type": "array"
},
"cc": {
"description": "CC recipients (max 100)",
"description": "CC recipients (subject to 50 total To, CC, and BCC recipients)",
"items": {
"$ref": "#/components/schemas/Recipient"
},
"maxItems": 100,
"maxItems": 49,
"type": "array"
},
"custom_headers": {
Expand Down