X Enterprises

fastify-xwhatconverts

WhatConverts lead tracking plugin for Fastify — lead CRUD, account management, profile management, and call recording retrieval.

fastify-xwhatconverts

WhatConverts lead tracking integration for Fastify v5. Decorates fastify.xWhatConverts with four service namespaces: leads (CRUD + constants), accounts (Agency Key required), profiles, and recordings. All methods validate inputs, attach the original WhatConverts error object on API failure, and expose structured error codes.

Installation

npm install @xenterprises/fastify-xwhatconverts

Quick Start

import Fastify from "fastify";
import xWhatConverts from "@xenterprises/fastify-xwhatconverts";

const fastify = Fastify({ logger: true });

await fastify.register(xWhatConverts, {
  token: process.env.WHATCONVERTS_TOKEN,
  secret: process.env.WHATCONVERTS_SECRET,
});

// List recent phone call leads
const result = await fastify.xWhatConverts.leads.list({
  leadType: "phone_call",
  leadsPerPage: 50,
});

await fastify.listen({ port: 3000 });

Options

NameTypeDefaultRequiredDescription
tokenstringYesWhatConverts API token.
secretstringYesWhatConverts API secret.
baseUrlstring"https://app.whatconverts.com/api/v1"NoAPI base URL override.
activebooleantrueNoSet false to skip plugin registration.

Methods

Leads (fastify.xWhatConverts.leads)

Accounts (fastify.xWhatConverts.accounts) — Agency Key required

Profiles (fastify.xWhatConverts.profiles)

Recordings (fastify.xWhatConverts.recordings)

Error Reference

ErrorCause
xwhatconverts: missing required option `token` (string), e.g. `app.register(xWhatConverts, { token: 'wc-token', secret: 'wc-secret' })` Missing or non-string token at registration
xwhatconverts: missing required option `secret` (string), e.g. `app.register(xWhatConverts, { token: 'wc-token', secret: 'wc-secret' })` Missing or non-string secret at registration
xwhatconverts: option `baseUrl` must be a string, e.g. `app.register(xWhatConverts, { token: 'wc-token', secret: 'wc-secret', baseUrl: 'https://app.whatconverts.com/api/v1' })` Non-string baseUrl at registration
[xWhatConverts] leads.get: leadId is requiredleads.get() called without leadId
[xWhatConverts] leads.create: params object is requiredleads.create() called without a params object
[xWhatConverts] leads.create: profileId is requiredleads.create() missing profileId
[xWhatConverts] leads.create: leadType is requiredleads.create() missing leadType
[xWhatConverts] leads.update: leadId is requiredleads.update() called without leadId
[xWhatConverts] leads.update: params object is requiredleads.update() called without a params object
[xWhatConverts] leads.delete: leadId is requiredleads.delete() called without leadId
[xWhatConverts] accounts.get: accountId is requiredaccounts.get() called without accountId
[xWhatConverts] accounts.create: params object is requiredaccounts.create() called without a params object
[xWhatConverts] accounts.create: accountName is requiredaccounts.create() missing accountName
[xWhatConverts] accounts.update: accountId is requiredaccounts.update() called without accountId
[xWhatConverts] accounts.update: params object is requiredaccounts.update() called without a params object
[xWhatConverts] accounts.delete: accountId is requiredaccounts.delete() called without accountId
[xWhatConverts] profiles.get: profileId is requiredprofiles.get() called without profileId
[xWhatConverts] profiles.create: params object is requiredprofiles.create() called without a params object
[xWhatConverts] profiles.create: accountId is requiredprofiles.create() missing accountId
[xWhatConverts] profiles.create: profileName is requiredprofiles.create() missing profileName
[xWhatConverts] profiles.update: profileId is requiredprofiles.update() called without profileId
[xWhatConverts] profiles.update: params object is requiredprofiles.update() called without a params object
[xWhatConverts] profiles.delete: profileId is requiredprofiles.delete() called without profileId
[xWhatConverts] recordings.get: leadId is requiredrecordings.get() called without leadId
[xWhatConverts] recordings.getBuffer: leadId is requiredrecordings.getBuffer() called without leadId
[xWhatConverts] recordings.getUrl: leadId is requiredrecordings.getUrl() called without leadId
[xWhatConverts] Network error: <message>Network-level failure (DNS, timeout, connection refused); error has code: "XWHATCONVERTS_NETWORK_ERROR"
[xWhatConverts] API error: <status>Non-2xx response from WhatConverts API; error has code: "XWHATCONVERTS_API_ERROR" plus statusCode and whatConvertsError

Environment Variables

The plugin never reads process.env. All configuration arrives via app.register(xWhatConverts, options). These variables are a consumer-side convention — your app owns env access and passes the values in as options:

VariablePassed asDescription
WHATCONVERTS_TOKENtokenAPI token from the WhatConverts dashboard.
WHATCONVERTS_SECRETsecretAPI secret from the WhatConverts dashboard.
await fastify.register(xWhatConverts, {
  token: process.env.WHATCONVERTS_TOKEN,
  secret: process.env.WHATCONVERTS_SECRET,
});

How It Works

On registration the plugin validates token and secret, builds a Basic Auth header from them, then decorates fastify.xWhatConverts with four service namespaces (leads, accounts, profiles, recordings). All JSON service methods share a common apiRequest helper that sets Authorization and Content-Type, handles both JSON and empty responses, and wraps failures as structured errors with code, statusCode, and whatConvertsError. The recordings service uses apiBinaryRequest which returns the raw response body as an ArrayBuffer. API credentials are held in closure scope and are never exposed on the decorator.

AI Context

package: "@xenterprises/fastify-xwhatconverts"
type: fastify-plugin
use-when: WhatConverts lead tracking — lead CRUD, account management (Agency Key), profile management, call recording retrieval
decorator: fastify.xWhatConverts (leads, accounts, profiles, recordings)
env: WHATCONVERTS_TOKEN, WHATCONVERTS_SECRET
accounts: requires Agency Key (token/secret from agency account)
error-shape: { code, statusCode, whatConvertsError } on API failures
Copyright © 2026