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
20 changes: 20 additions & 0 deletions .coderabbit.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
language: en-US
early_access: false
reviews:
profile: chill
high_level_summary: true
poem: false
review_status: true
collapse_walkthrough: false
path_filters:
- "!**/pnpm-lock.yaml"
- "!apps/docs/images/**"
- "!**/*.png"
- "!**/*.jpg"
- "!**/*.jpeg"
- "!**/*.webp"
auto_review:
enabled: true
drafts: false
chat:
auto_reply: true
19 changes: 19 additions & 0 deletions .github/workflows/coderabbit.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
name: CodeRabbit Review

on:
pull_request:
types: [opened, reopened, synchronize, ready_for_review]

permissions:
contents: read
pull-requests: write

jobs:
review:
if: github.event.pull_request.draft == false
runs-on: ubuntu-latest
steps:
- name: Run CodeRabbit review
uses: coderabbitai/coderabbit-action@v2
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
7 changes: 2 additions & 5 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,4 @@
{
"eslint.workingDirectories": [
{
"mode": "auto"
}
]
"editor.formatOnSave": true,
"editor.formatOnPaste": true,
}
85 changes: 84 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,88 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

---

## [0.2.6] - 2026-05-10

### Added

#### Marketing Components
- **Modular homepage architecture** — split the landing page into reusable section components (`Hero`, `TrustStrip`, `Features`, `Comparison`, `PricingSection`, `CallToAction`, `DevSection`) for clearer ownership and easier iteration
- **Single pricing calculator flow** — promoted the calculator-led pricing experience and removed older card-based pricing composition in favor of a unified pricing section

#### Dashboard — Broadcasts
- **Broadcasts section** — introduced a dedicated `/broadcasts` route and page hierarchy (`/broadcasts`, `/broadcasts/[broadcastId]`, `/broadcasts/[broadcastId]/edit`) for one-off email blasts, separating them from the Campaigns flow which is now focused on recurring/automated sequences

#### Dashboard — Logs
- **Unified logs dashboard** — added a first-class `/logs` dashboard page that merges email events, webhook calls, and notification delivery logs into one searchable audit trail with source and status filtering

#### Dashboard Settings
- **Settings API Keys page** — added first-class `/settings/api-keys` route
- **Settings SMTP page** — added first-class `/settings/smtp` route

#### Billing — Custom Plan Contracts
- **Slider-based plan selector** — `BillingPlanSelector` component replaces static plan cards at `/settings/billing` with interactive marketing and transactional email limit sliders (1,000 – 3,000,000 each); calculated contract total updates in real time as limits are adjusted
- **Custom Stripe checkout sessions** — `createCustomCheckoutSession` tRPC mutation creates a Stripe session with `price_data` (unit_amount = exact contract price in CAD) rather than a fixed price ID, locking teams into the exact limit/price combination they selected
- **Custom plan query** — `getCustomPlanContract` tRPC query lets the billing page and `PlanDetails` component surface the active custom contract (limits + monthly price) for teams already on a custom plan
- **Custom plan DB fields** — added four optional fields to the `Team` model (`customPlanEnabled`, `customMarketingEmailLimit`, `customTransactionalEmailLimit`, `customMonthlyPriceCents`); populated at checkout completion via the Stripe webhook

#### Campaigns
- **Campaign intent field** — campaigns now store an `intent` column (migration `20260510120000_add_campaign_intent`) to distinguish campaign purpose at creation time; `create-campaign` dialog captures intent upfront

#### SDKs
- **Go SDK initial package** — introduced `packages/go-sdk` with typed client surfaces for emails, contacts, contact books, campaigns, domains, and analytics

#### Documentation
- **SMTP auth API reference page** — added `apps/docs/api-reference/smtp/auth.mdx`
- **Self-hosting Docker doc relocation** — promoted Docker setup docs to `apps/docs/self-hosting/docker.mdx` and aligned navigation

#### CI / Review Automation
- **CodeRabbit support** — added `.github/workflows/coderabbit.yml` and root `.coderabbit.yaml` to enable automated PR review summaries and inline feedback on pull requests

### Changed

#### Marketing Site
- **Homepage composition refresh** — replaced older section files (`FeatureCard*`, `PricingTiers`, `CodeExample`) with the new component set and updated page assembly
- **Icon system modernization** — replaced hand-authored inline SVG usage in key marketing and app screens with icon components for consistency and maintainability
- **Developer section improvements** — expanded dev-focused section behavior and language-toggle handling in `CodeLangToggle` and `DevSection`

#### Settings UX & Navigation
- **Developer settings consolidation** — aligned developer tooling pages with the canonical Settings area while preserving compatibility flows from legacy dev-settings routes

#### Plans, Billing & Limits
- **Pricing/plan constant updates** — refreshed shared Stripe plan/product definitions and app-side plan/payment constants to keep UI, checkout, and limits in sync
- **Billing page rebuilt around custom plan selector** — `/settings/billing` now surfaces the slider-based `BillingPlanSelector` as the primary upgrade path; `PlanDetails` shows active custom contract details (limits and monthly price) when a custom plan is active
- **Custom plan limit enforcement** — `LimitService` now reads `customMarketingEmailLimit` and `customTransactionalEmailLimit` from the `Team` record and enforces per-type monthly caps for custom plan teams, bypassing the standard plan-tier daily limits; the daily usage job skips metered overage billing for custom plan teams to avoid double-charging
- **Admin plan assignment clears custom contract** — `adminAssignPlan` with `method: "complimentary"` now explicitly clears all custom plan fields (`customPlanEnabled`, all limit columns) so stale slider contracts do not persist after a manual admin override
- **Free-plan marketing access switched to limit-based enforcement** — Contacts, Broadcasts, and Campaigns are no longer hard-locked in the dashboard for Free teams; access is now controlled by enforceable plan limits (including campaign count and monthly sending caps)
- **Free plan marketing baseline updated** — FREE plan now includes limited marketing capability (`marketingEmailsIncluded: true`) with a capped campaign allowance (`campaignsLimit: 3`) instead of full marketing exclusion
- **Stripe seed updates** — adjusted `stripe-seed.ts` for current product/price setup behavior

#### Analytics
- **Analytics detail expansion** — dashboard analytics now includes additional breakdown cards (delivery rate, bounce rate, complaint rate, and total volume) for clearer at-a-glance performance tracking
- **Paid-only advanced analytics insights** — added a paid-tier insights section for open rate, click rate, click-to-open rate, and average daily volume with an upgrade CTA for free teams

#### Usage Breakdown
- **Full usage limit breakdown** — the `/settings/usage` page now shows a complete limit audit for all tracked resources, including monthly and daily email usage, marketing vs transactional email usage, domains, contact books, contacts, campaigns, team members, and webhooks, so teams can see both current usage and allowed limits in one place

#### Documentation & API Spec
- **OpenAPI refresh** — regenerated and updated API reference spec and intro content
- **Docs navigation/content refresh** — updated docs navigation and onboarding pages (including Go and self-hosting paths) to match current product structure

### Fixed

#### Dashboard & Routing
- **Settings route reliability** — removed dead-end developer settings destinations by wiring pages into canonical Settings routes and maintaining redirect compatibility

#### Billing & Limits
- **Admin bypass email normalization** — `LimitService.isAdminOrFounderTeam` now normalises the environment admin email with `trim()` + `toLowerCase()` and uses a case-insensitive Prisma query, preventing mismatches caused by casing differences in env config
- **Custom plan Stripe webhook sync** — `syncStripeData` now reads `customPlanEnabled`, `customMarketingEmailLimit`, `customTransactionalEmailLimit`, and `customMonthlyPriceCents` from subscription metadata and persists them back to the `Team` record so contract limits survive server restarts and are always in sync with Stripe

#### Visual Consistency
- **Cross-page icon sizing/alignment** — normalized icon rendering across footer, auth, error, not-found, and dashboard surfaces after component migration
- **UpgradeModal scroll** — fixed scroll behavior in the upgrade and notification modals so long plan/feature lists are fully accessible without clipping

---

## [0.2.5] - 2026-05-09

### Added
Expand Down Expand Up @@ -515,7 +597,8 @@ Initial public beta release of ByteSend — an all-in-one email infrastructure p

---

[Unreleased]: https://github.com/nodebyte/bytesend/compare/v0.2.5...HEAD
[Unreleased]: https://github.com/nodebyte/bytesend/compare/v0.2.6...HEAD
[0.2.6]: https://github.com/nodebyte/bytesend/compare/v0.2.5...v0.2.6
[0.2.5]: https://github.com/nodebyte/bytesend/compare/v0.2.4...v0.2.5
[0.2.4]: https://github.com/nodebyte/bytesend/compare/v0.2.3...v0.2.4
[0.2.3]: https://github.com/nodebyte/bytesend/compare/v0.2.2...v0.2.3
Expand Down
13 changes: 12 additions & 1 deletion apps/docs/api-reference/introduction.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -21,4 +21,15 @@ Authentication to ByteSend's API is performed via the Authorization header with
Authorization: Bearer bs_12345
```

You can create a new token/API key under your ByteSend [Developer Settings](https://bytesend.cloud/dev-settings/api-keys).
You can create a new token/API key under your ByteSend [Settings > API Keys](https://bytesend.cloud/settings/api-keys).

### SMTP Authentication

The SMTP relay server uses a different authentication method. Instead of Bearer tokens, SMTP clients authenticate using:

- **Username**: The team's custom SMTP username (or the default `bytesend`)
- **Password**: Your API key

The SMTP relay calls the [SMTP Auth endpoint](/api-reference/smtp/auth) to validate these credentials. This endpoint is **public** and does not require Bearer token authentication.

For more details, see the [SMTP relay documentation](/self-hosting/smtp-server).
57 changes: 57 additions & 0 deletions apps/docs/api-reference/openapi.json
Original file line number Diff line number Diff line change
Expand Up @@ -2679,6 +2679,63 @@
}
}
}
},
"/v1/smtp/auth": {
"post": {
"description": "Authenticate SMTP credentials (used by SMTP relay server)",
"requestBody": {
"required": true,
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"username": {
"type": "string",
"description": "SMTP username (typically the custom team username or default 'bytesend')"
},
"password": {
"type": "string",
"description": "API key used as password for SMTP authentication"
}
},
"required": ["username", "password"]
}
}
}
},
"responses": {
"200": {
"description": "Credentials are valid",
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"valid": { "type": "boolean", "enum": [true] }
},
"required": ["valid"]
}
}
}
},
"401": {
"description": "Invalid credentials",
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"valid": { "type": "boolean", "enum": [false] },
"message": { "type": "string", "description": "Error message explaining why authentication failed" }
},
"required": ["valid", "message"]
}
}
}
}
}
}
}
}
}
88 changes: 88 additions & 0 deletions apps/docs/api-reference/smtp/auth.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
---
openapi: post /v1/smtp/auth
---

Authenticate SMTP credentials. This endpoint is called by the SMTP relay server to validate client credentials during authentication.

## Usage

The SMTP relay server calls this endpoint during the `AUTH` phase to verify that the provided username and API key are valid. The username can be a custom team-specific SMTP username or the global default (`bytesend`).

## Authentication

This endpoint **does not require** Bearer token authentication. Instead, it accepts the credentials directly in the request body.

## Request Body

- `username` (string, required): The SMTP username. Can be:
- A custom team SMTP username (configured in team settings)
- The global default: `bytesend`
- Or any username if using an API key for a team with remote auth enabled

- `password` (string, required): The API key to verify. This is used as the password in SMTP `AUTH PLAIN` or `AUTH LOGIN` flows.

## Response

**200 Success:**
```json
{
"valid": true
}
```

**401 Unauthorized:**
```json
{
"valid": false,
"message": "Invalid API key or username"
}
```

## Examples

<CodeGroup>

```bash cURL
curl -X POST https://bytesend.cloud/api/v1/smtp/auth \
-H "Content-Type: application/json" \
-d '{
"username": "bytesend",
"password": "YOUR_API_KEY"
}'
```

```javascript JavaScript
const response = await fetch('https://bytesend.cloud/api/v1/smtp/auth', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
username: 'bytesend',
password: 'YOUR_API_KEY'
})
});

const data = await response.json();
console.log(data); // { valid: true } or { valid: false, message: "..." }
```

```python Python
import requests

response = requests.post(
'https://bytesend.cloud/api/v1/smtp/auth',
json={
'username': 'bytesend',
'password': 'YOUR_API_KEY'
}
)

print(response.json()) # { 'valid': True } or { 'valid': False, 'message': '...' }
```

</CodeGroup>

## Notes

- This endpoint is **fire-and-forget** from the perspective of the SMTP relay. Failures are logged but do not block the SMTP handshake.
- The endpoint does not perform rate limiting separate from the standard API rate limits.
- For custom team SMTP usernames, ensure the API key belongs to that team.
22 changes: 12 additions & 10 deletions apps/docs/docs.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"$schema": "https://mintlify.com/docs.json",
"theme": "mint",
"theme": "maple",
"name": "ByteSend",
"colors": {
"primary": "#0066CC",
Expand Down Expand Up @@ -31,11 +31,11 @@
"group": "Getting Started",
"pages": [
"introduction",
"get-started/smtp",
"get-started/go",
"get-started/nodejs",
"get-started/python",
"get-started/local",
"get-started/set-up-docker"
"get-started/smtp"
]
}
]
Expand All @@ -47,6 +47,7 @@
"group": "Basic Setup",
"pages": [
"self-hosting/overview",
"self-hosting/docker",
"self-hosting/smtp-server",
"self-hosting/certificates",
"self-hosting/firewall",
Expand Down Expand Up @@ -162,6 +163,12 @@
"api-reference/analytics/email-time-series",
"api-reference/analytics/reputation-metrics"
]
},
{
"group": "SMTP",
"pages": [
"api-reference/smtp/auth"
]
}
]
}
Expand All @@ -175,19 +182,14 @@
"icon": "github"
},
{
"anchor": "Community",
"anchor": "Discord",
"href": "https://discord.gg/xqkqzVRC4S",
"icon": "discord"
},
{
"anchor": "Twitter",
"href": "https://twitter.com/TryByteSend",
"icon": "twitter"
},
{
"anchor": "Dashboard",
"href": "https://bytesend.cloud",
"type": "button"
}
]
}
Expand Down Expand Up @@ -216,7 +218,7 @@
],
"primary": {
"type": "button",
"label": "Dashboard",
"label": "Website",
"href": "https://bytesend.cloud"
}
},
Expand Down
Loading
Loading