Skip to content

Alpha-Jolt/Mail-Bridge

Repository files navigation

Mail-Bridge

Standalone email application service. Validates, composes, sends, and tracks job application emails via a pluggable registry adapter.

What It Does

Mail-Bridge handles the full lifecycle of sending a job application email in a single API call:

  1. Validates the request — checks the resume variant is approved, the job is open and has a contact email, the user hasn't already applied, and rate limits are respected
  2. Composes a professional HTML email from a Handlebars template using job and user data
  3. Attaches the resume PDF via a signed URL fetched from the registry
  4. Sends the email through SendGrid
  5. Records the application back to the registry for tracking

All data access goes through a RegistryAdapter interface, so Mail-Bridge has no direct database dependency and can be wired to any backend.

Getting Started

Prerequisites

  • Node.js 18+
  • A SendGrid API key
  • A registry service (or use REGISTRY_TYPE=memory for local testing without one)

Setup

git clone <repo-url> && cd mail-bridge
npm install
cp .env.example .env

Open .env and fill in at minimum:

SENDGRID_API_KEY=SG.your-key-here
REGISTRY_URL=http://your-registry-service/api
REGISTRY_API_KEY=your-registry-key

To run locally without a registry service, set REGISTRY_TYPE=memory instead — data lives in memory and resets on restart.

Run

npm run dev        # development (ts-node)
npm run build      # compile TypeScript
npm start          # run compiled output

Verify it's running

curl http://localhost:3000/health
# → { "status": "ok", "version": "0.1.0", ... }

curl http://localhost:3000/health/ready
# → { "status": "ready", "registry": "ok" }   (200 if registry reachable, 503 if not)

Sending an Application

Mail-Bridge provides two endpoints for sending applications:

1. New Self-Contained Endpoint (Recommended)

POST /api/mail/send — Accepts all data in the request payload. No registry lookups for variant/job data.

curl -X POST http://localhost:3000/api/mail/send \
  -H "Content-Type: application/json" \
  -d '{
    "to_email": "hr@company.com",
    "template": "standard",
    "context": {
      "user": {
        "name": "John Doe",
        "email": "john@example.com",
        "phone": "9876543210",
        "summary": "3 years experience in Python and FastAPI"
      },
      "job": {
        "title": "Python Developer",
        "company_name": "Acme Corp",
        "location": "Bangalore"
      }
    },
    "attachments": [
      {
        "filename": "resume.pdf",
        "signed_url": "https://s3.example.com/resumes/user-123/resume.pdf?expires=900"
      }
    ]
  }'

Success response:

{ "success": true, "message_id": "sendgrid-msg-id", "sent_at": "2026-05-07T10:00:00Z" }

Error codes: INVALID_PAYLOAD · INVALID_EMAIL · TEMPLATE_NOT_FOUND · NO_ATTACHMENTS · ATTACHMENT_DOWNLOAD_FAILED · SEND_FAILED

2. Legacy Registry-Based Endpoint

POST /api/applications/send — Requires variant/job IDs. Fetches data from registry and enforces 5 validation gates.

curl -X POST http://localhost:3000/api/applications/send \
  -H "Content-Type: application/json" \
  -d '{"variant_id":"var-123","job_id":"job-456","user_id":"user-789"}'

On success you get back the message ID and sent timestamp:

{ "success": true, "message_id": "...", "sent_at": "..." }

If a validation check fails, no email is sent and you get a specific error code:

{ "success": false, "error": { "code": "VARIANT_NOT_APPROVED", "message": "..." } }

Error Codes: VARIANT_NOT_FOUND · VARIANT_NOT_APPROVED · JOB_NOT_FOUND · JOB_CLOSED · JOB_NO_EMAIL · DUPLICATE_APPLICATION · DAILY_LIMIT_EXCEEDED · RATE_LIMIT_TOO_FAST · INVALID_REQUEST · INTERNAL_ERROR

Rate limit errors return HTTP 429; all others return 400.

Configuration

Variable Default Description
PORT 3000 Server port
NODE_ENV development development / production
REGISTRY_TYPE rest rest (HTTP) or memory (in-process, no persistence)
REGISTRY_URL Registry service base URL
REGISTRY_API_KEY Registry Bearer token
SENDGRID_API_KEY SendGrid API key
SENDER_EMAIL noreply@example.com From address on all emails
SENDER_NAME Mail-Bridge From display name
MAX_APPLICATIONS_PER_DAY 10 Max emails a user can send per day
MIN_SECONDS_BETWEEN_SENDS 30 Minimum seconds between consecutive sends
LOG_LEVEL info debug / info / warn / error

Docker

Build and run the image:

docker build -t mail-bridge:latest .
docker run -p 3000:3000 \
  -e SENDGRID_API_KEY=SG.your-key \
  -e REGISTRY_TYPE=memory \
  mail-bridge:latest

Or use Docker Compose for a one-command start:

docker-compose up -d
docker-compose logs -f mail-bridge

Extending

Mail-Bridge is registry-agnostic. To connect it to a different data source, implement the RegistryAdapter interface in src/adapters/ and register it in src/server.ts. The interface requires methods to fetch variants, jobs, and signed URLs, check for duplicates, enforce rate limits, and save application records.

About

Mail Engine validates, composes, sends, and tracks emails via a pluggable registry interface, and is fully decoupled, configurable, and reusable across any project.

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Contributors