Freshsales

Lightweight, dependency-free, in-memory fake of the Freshsales (Freshworks CRM) API for testing code that talks to the Freshsales REST API directly.

Default port: 4783

Quick start

import { FreshsalesServer } from "./services/freshsales/src/server.js";

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

Point a client at it (auth via the Token token= header scheme):

const base = "http://127.0.0.1:4783";
const res = await fetch(`${base}/api/contacts`, {
  method: "POST",
  headers: { Authorization: "Token token=pat-parlel", "Content-Type": "application/json" },
  body: JSON.stringify({ contact: { first_name: "Ada", last_name: "Lovelace", email: "ada@parlel.dev" } }),
});
// => { contact: { id, first_name, ... } }

Access via MCP / preview URL

Plain HTTP at http://127.0.0.1:4783, reachable through the parlel MCP/preview proxy under the slug freshsales.

Implemented operations

All /api/* routes require Authorization: Token token=<api-key> (or Bearer). State is in-memory and ephemeral.

Single resources are wrapped under the singular key ({ contact: {...} }); collections under the plural key with meta ({ contacts: [...], meta: { total_pages, total } }).

Contacts — /api/contacts

Leads — /api/leads

CRUD surface (same name/email requirement).

Deals — /api/deals

CRUD surface (name required on create).

Sales accounts — /api/sales_accounts

CRUD surface (name required on create).

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
Contacts / Leads / Deals / Sales accounts CRUD✅ Supported
Token token= header auth + Bearer✅ Supported
Wrapped single + collection envelopes (+ meta)✅ Supported
Required-field validation (400 with errors)✅ Supported
Filtered/view-based listing, lookups, search⟳ Roadmap
Sales activities / appointments / tasks / notes⟳ Roadmap
Pagination beyond a single pagetotal_pages: 1
API-key validity◐ Any well-formed token accepted

Error codes & shapes

Errors use the Freshsales envelope { errors: { message: [...], <field>: [...] } }.

StatusWhen
400malformed JSON / validation failure
401no Token token= / Bearer auth
404unknown id / resource
405method not allowed for the path

Manifest

See services/freshsales/manifest.json: name freshsales, port 4783, protocol http, healthcheck /health, startup ≈ 100ms, env FRESHSALES_API_KEY, FRESHSALES_BASE_URL.

<!-- 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.

FRESHSALES_API_KEY=pat-parlel
FRESHSALES_BASE_URL=http://parlel-bridge:4783
<!-- parlel:testenv:end -->