Brevo

Lightweight, dependency-free, in-memory Brevo (formerly Sendinblue) API v3 fake for testing code that uses the real @getbrevo/brevo SDK (or the language-agnostic Brevo REST API).

Default port: 4828

Quick start

Start the server:

import { BrevoServer } from "./services/brevo/src/server.js";

const server = new BrevoServer(4828);
await server.start();
// ... run your app/tests ...
await server.stop();

Point the real @getbrevo/brevo client at it (override basePath / send api-key):

await fetch("http://127.0.0.1:4828/v3/smtp/email", {
  method: "POST",
  headers: { "api-key": "xkeysib-parlel", "Content-Type": "application/json" },
  body: JSON.stringify({
    sender: { email: "sender@parlel.dev", name: "Parlel" },
    to: [{ email: "recipient@parlel.dev" }],
    subject: "Hello",
    htmlContent: "<b>Hi</b>",
  }),
});
// => { messageId: "<...@parlel.brevo>" }

Every send is captured and inspectable via /__parlel/*.

Access via MCP / preview URL

When run under the parlel pool, this service is reachable through the MCP gateway and a preview URL at http://127.0.0.1:4828. Use BREVO_BASE_URL to point clients/agents at it. Captured mail lives at GET /__parlel/messages.

Implemented operations

All /v3/* routes require the api-key header. State is in-memory and ephemeral.

Service & inspection operations (parlel extensions)

Surface coverage

This emulator faithfully replicates the API surface most application code and agents exercise. Anything below the supported lines is either an intentional design choice for a fast, zero-cost local emulator (✓ By design) or a candidate for a future release (⟳ Roadmap) — never a silent inaccuracy.

Legend: ✅ fully supported · ◐ accepted (stored, not strictly enforced) · ✓ by design · ⟳ on the roadmap.

FeatureStatus
sendTransacEmail (POST /v3/smtp/email)✅ Supported
Contacts CRUD (create/list/get/update/delete)✅ Supported
Template creation✅ Supported
Account info✅ Supported
Captured-mail inspection✅ Supported (parlel extension)
Actual email delivery / SMTP✓ By design — Captured in-memory for inspection — no real messages sent
Real template rendering◐ Accepted; not rendered
Lists / folders / campaigns / webhooks / stats⟳ Roadmap
Real API-key validity / scope enforcement✓ By design — Any non-empty credential is accepted — no real secrets needed
Rate limiting (429)✓ By design — Never throttles — local tests run at full speed, zero cost

Error shapes

Errors use the Brevo envelope { "code": "...", "message": "..." }.

StatuscodeWhen
401unauthorizedmissing/empty api-key
400missing_parameterrequired sender/to/templateName missing
400invalid_parameterinvalid email or malformed body
400duplicate_parametercontact already exists
404document_not_foundunknown contact

Manifest

See services/brevo/manifest.json:

<!-- parlel:testenv:start -->

Configuration — test.env

Copy these into your test.env (used by the bridge sidecar flow). Tokens are Parlel's seeded test credentials — any non-empty value is accepted by the emulator, so you rarely need to change them. Swap in real credentials only when pointing at the live service in prod.env.

BREVO_API_KEY=xkeysib-parlel
BREVO_BASE_URL=http://parlel-bridge:4828
<!-- parlel:testenv:end -->