Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
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
418 changes: 418 additions & 0 deletions connectors/companyenrich/connector.test.ts

Large diffs are not rendered by default.

56 changes: 56 additions & 0 deletions connectors/companyenrich/endpoints/enrich-company/endpoint.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import { defineEndpoint, Unit, UsageModelKind } from "@shared/core";
import { zEnrichCompanyQuery } from "./schema/inputs.ts";

export default defineEndpoint({
meta: {
displayName: "Enrich a Company",
summary: "Return a company profile from its domain.",
description:
"Enrich a known company domain with firmographics, location, funding, technologies and social profiles. Fields vary by company. Costs 1 credit per successful call; expand=workforce adds 5 credits. With waitForEnrichment=false, an unstored domain returns 404 without charging credits and schedules enrichment for a later lookup. Use companyenrich#companies/search when you have criteria rather than a known domain.",
docsUrl:
"https://docs.companyenrich.com/reference/get_companies-enrich",
categories: ["company-enrichment"],
},
request: { method: "GET", path: "/companies/enrich" },
input: {
schema: {
queryParams: zEnrichCompanyQuery,
},
},
usage: {
model: {
kind: UsageModelKind.COMPOSITE,
components: {
call: {
kind: UsageModelKind.PER_CALL,
consumes: { credit: "default", amount: 1 },
label: "successful lookup",
},
workforce: {
kind: UsageModelKind.PER_UNIT,
unit: Unit.RESULT,
consumes: { credit: "default", amount: 5 },
label: "workforce expansion",
},
},
},
estimate: ({ data }) => ({
counts: {
workforce: data.input.queryParams.expand?.includes("workforce")
? 1
: 0,
},
}),
evidence: ({ data }) => {
const expansion = data.input.queryParams?.expand;
return {
counts: {
workforce: Array.isArray(expansion) &&
expansion.includes("workforce")
? 1
: 0,
},
};
},
},
});
14 changes: 14 additions & 0 deletions connectors/companyenrich/endpoints/enrich-company/schema/inputs.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { z } from "zod";

/** Vendor input mirror: https://docs.companyenrich.com/reference/get_companies-enrich
* OpenAPI checked 2026-10-07; no connector defaults in this mirror. */

export const zEnrichCompanyQuery = z.object({
domain: z.string().max(1024),
expand: z.array(z.enum(["workforce"])).describe(
"Expandable response fields. Repeat the parameter to request multiple expansions.\n\nSupported values:\n- `workforce`: costs 5 credits per company and adds the `workforce` field to `CompanyInfo`.",
).optional(),
waitForEnrichment: z.boolean().describe(
"Whether to wait for on-demand enrichment when the company is not stored yet. When false, stored company data is returned immediately; a company that is not stored yet results in a 404 without charging credits, and its enrichment is scheduled in the background so a later call can return it quickly.",
).optional(),
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
import { defineEndpoint, Unit, UsageModelKind } from "@shared/core";
import {
zCompanySimilarPageInput,
zSimilarCompaniesQuery,
} from "./schema/inputs.ts";

export default defineEndpoint({
meta: {
displayName: "Find Similar Companies",
summary:
"Discover lookalike companies from seed domains and optional filters.",
description:
"Find lookalike companies using 1–10 seed domains in domains, optional similarity controls and company filters. The older singular domain remains accepted but is deprecated by the vendor. Returns company profiles and similarity metadata. Set pageSize explicitly (1–100). Costs 5 credits per returned company, with a 5-credit minimum; expand=workforce adds 5 credits per company. Missing seed enrichment can return 404 when waitForEnrichment=false.",
docsUrl:
"https://docs.companyenrich.com/reference/post_companies-similar",
categories: ["company-enrichment"],
},
request: { method: "POST", path: "/companies/similar" },
input: {
schema: {
body: zCompanySimilarPageInput.required({ pageSize: true }),
queryParams: zSimilarCompaniesQuery,
},
},
usage: {
model: {
kind: UsageModelKind.COMPOSITE,
components: {
result: {
kind: UsageModelKind.PER_UNIT,
unit: Unit.RESULT,
consumes: { credit: "default", amount: 5 },
label: "results (minimum one)",
},
workforce: {
kind: UsageModelKind.PER_UNIT,
unit: Unit.RESULT,
consumes: { credit: "default", amount: 5 },
label: "workforce expansion",
},
},
},
estimate: ({ data }) => ({
counts: {
result: data.input.body.pageSize,
workforce: data.input.queryParams.expand?.includes("workforce")
? data.input.body.pageSize
: 0,
},
}),
evidence: ({ data, utils }) => {
const delivered = utils.json.len(data.output, "$.items");
const expansion = data.input.queryParams?.expand;
return {
counts: {
// Only the base line has the vendor minimum; expansions
// count delivered records, never totalItems or pageSize.
result: Math.max(1, delivered),
workforce: Array.isArray(expansion) &&
expansion.includes("workforce")
? delivered
: 0,
},
};
},
},
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
import { z } from "zod";
import {
zCompanyCategory,
zCompanyEmployees,
zCompanyExcludeFilters,
zCompanyFundingAmountRange,
zCompanyFundingRound,
zCompanyRevenue,
zCompanyType,
zCompanyWorkforceGrowthInput,
zCompanyWorkforceSizeInput,
zFeatureRequirement,
zNullableOfCompanyWorkforceGrowthDepartment,
zNullableOfCompanyWorkforceGrowthDepartment2,
zNullableOfCompanyWorkforceGrowthPeriod,
zNullableOfFilterOperator,
zYearRange,
} from "../../../schema/common.ts";

/** Vendor input mirror: https://docs.companyenrich.com/reference/post_companies-similar
* OpenAPI checked 2026-10-07; no connector defaults in this mirror. */

export const zCompanySimilarPageInput = z.object({
domain: z.string().nullable().describe(
"The domain to find similar companies for. This is deprecated, use `domains` instead. Using `domains` with a single domain acts the same as using the `domain` parameter.",
).optional(),
page: z.number().int().describe(
"The page number to return. Must be greater than 0",
).optional(),
pageSize: z.number().int().min(1).max(100).describe(
"The number of results to return. Must be between 1 and 100",
).optional(),
domains: z.array(z.string()).min(1).max(10).describe(
"The domains to find similar companies for. Up to 10 domains are allowed.",
).optional(),
similarityWeight: z.number().min(-1).max(1).describe(
"The similarity weight to apply to the results. Must be between -1 and 1. 0 is default. Larger values will prioritize more similar companies, smaller values will prioritize more established companies.",
).optional(),
minScore: z.number().min(0).max(1).nullable().describe(
"The minimum similarity score required for results. Must be between 0 and 1. When omitted, the default cutoff is used.",
).optional(),
exclude: zCompanyExcludeFilters.optional(),
query: z.string().max(250).nullable().describe(
"The search query to apply on the company name and domain",
).optional(),
foundedYear: zYearRange.optional(),
fundingAmount: zCompanyFundingAmountRange.optional(),
fundingYear: zYearRange.optional(),
categoryOperator: zNullableOfFilterOperator.optional(),
keywordsOperator: zNullableOfFilterOperator.optional(),
technologiesOperator: zNullableOfFilterOperator.optional(),
workforceGrowth: zCompanyWorkforceGrowthInput.optional(),
workforceSize: z.array(zCompanyWorkforceSizeInput).nullable().describe(
"Filter companies by absolute workforce headcount. Multiple entries can be provided to filter on different departments simultaneously.",
).optional(),
require: z.array(zFeatureRequirement).nullable().describe(
"The features that must exist for the company",
).optional(),
regions: z.array(z.string()).nullable().describe(
"The region IDs to filter by",
).optional(),
countries: z.array(z.string()).nullable().describe(
"The 2 letter country codes to filter by",
).optional(),
states: z.array(z.number().int()).nullable().describe(
"The state IDs to filter by",
).optional(),
cities: z.array(z.number().int()).nullable().describe(
"The city IDs to filter by",
).optional(),
type: z.array(zCompanyType).nullable().describe(
"The list of company types to filter by",
).optional(),
category: z.array(zCompanyCategory).nullable().describe(
"The list of company categories to filter by",
).optional(),
employees: z.array(zCompanyEmployees).nullable().describe(
"The list of employee counts to filter by",
).optional(),
reportedEmployees: z.array(zCompanyEmployees).nullable().describe(
"The list of externally reported employee counts to filter by",
).optional(),
revenue: z.array(zCompanyRevenue).nullable().describe(
"The list of revenue ranges to filter by",
).optional(),
naicsCode: z.array(z.number().int()).nullable().describe(
"The NAICS codes to filter by. Can be 2 to 6 digit codes. In case of a 2-5 digit code, all 6 digit codes under it will be included",
).optional(),
keywords: z.array(z.string()).nullable().describe(
"The keywords to filter by",
).optional(),
technologies: z.array(z.string()).nullable().describe(
"The technologies to filter by",
).optional(),
fundingRounds: z.array(zCompanyFundingRound).nullable().describe(
"The funding rounds to filter by",
).optional(),
});

export const zSimilarCompaniesQuery = z.object({
expand: z.array(z.enum(["workforce"])).describe(
"Expandable response fields. Repeat the parameter to request multiple expansions.\n\nSupported values:\n- `workforce`: costs 5 credits per company and adds the `workforce` field to `CompanyInfo`.",
).optional(),
waitForEnrichment: z.boolean().describe(
"Whether to wait for on-demand enrichment when a seed company is not stored yet. When false, valid missing domains are scheduled for background enrichment and the request returns a 404 if any seed company is missing, so it can be retried with a stable seed set later. This only controls seed-company enrichment; preparing a similarity vector for a stored company may still require processing.",
).optional(),
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import { defineEndpoint, Unit, UsageModelKind } from "@shared/core";
import { zLookupPersonQuery, zPersonLookupRequest } from "./schema/inputs.ts";

export default defineEndpoint({
meta: {
displayName: "Look Up a Person by Email",
summary:
"Resolve a known email address to a matching professional profile.",
description:
"Resolve an email domain to a company, then match the local part to a person. Returns the best deterministic person match with employment context, or 404 when no match is found. This resolves a known email; it does not find a new work email for a person. Costs 5 credits per successful call; expand=education adds 1 credit. Use companyenrich#people/search to discover people from role and employer criteria.",
docsUrl: "https://docs.companyenrich.com/reference/post_people-lookup",
categories: ["people-enrichment"],
},
request: { method: "POST", path: "/people/lookup" },
input: {
schema: {
body: zPersonLookupRequest,
queryParams: zLookupPersonQuery,
},
},
usage: {
model: {
kind: UsageModelKind.COMPOSITE,
components: {
call: {
kind: UsageModelKind.PER_CALL,
consumes: { credit: "default", amount: 5 },
label: "successful lookup",
},
education: {
kind: UsageModelKind.PER_UNIT,
unit: Unit.RESULT,
consumes: { credit: "default", amount: 1 },
label: "education expansion",
},
},
},
estimate: ({ data }) => ({
counts: {
education: data.input.queryParams.expand?.includes("education")
? 1
: 0,
},
}),
evidence: ({ data }) => {
const expansion = data.input.queryParams?.expand;
return {
counts: {
education: Array.isArray(expansion) &&
expansion.includes("education")
? 1
: 0,
},
};
},
},
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { z } from "zod";

/** Vendor input mirror: https://docs.companyenrich.com/reference/post_people-lookup
* OpenAPI checked 2026-10-07; no connector defaults in this mirror. */

export const zPersonLookupRequest = z.object({
email: z.string().describe("The email address of the person to look up"),
});

export const zLookupPersonQuery = z.object({
expand: z.array(z.enum(["education"])).describe(
"Expandable response fields. Repeat the parameter to request multiple expansions.\n\nSupported values:\n- `education`: costs 1 credit per person and adds the `education` field to `PersonInfo`.",
).optional(),
});
67 changes: 67 additions & 0 deletions connectors/companyenrich/endpoints/search-companies/endpoint.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
import { defineEndpoint, Unit, UsageModelKind } from "@shared/core";
import {
zCompanySearchPageInput,
zSearchCompaniesQuery,
} from "./schema/inputs.ts";

export default defineEndpoint({
meta: {
displayName: "Search Companies",
summary:
"Find companies using natural-language queries and structured filters.",
description:
"Search companies with semanticQuery or filters for industry, location, size, funding and technology. Returns enriched profiles in items with pagination metadata. Set pageSize explicitly (1–100); page-based retrieval stops at 10,000 results. Costs 1 credit per returned company, with a 1-credit minimum; expand=workforce adds 5 credits per company. Use companyenrich#companies/similar to start from seed domains instead.",
docsUrl:
"https://docs.companyenrich.com/reference/post_companies-search",
categories: ["company-enrichment"],
},
request: { method: "POST", path: "/companies/search" },
input: {
schema: {
body: zCompanySearchPageInput.required({ pageSize: true }),
queryParams: zSearchCompaniesQuery,
},
},
usage: {
model: {
kind: UsageModelKind.COMPOSITE,
components: {
result: {
kind: UsageModelKind.PER_UNIT,
unit: Unit.RESULT,
consumes: { credit: "default", amount: 1 },
label: "results (minimum one)",
},
workforce: {
kind: UsageModelKind.PER_UNIT,
unit: Unit.RESULT,
consumes: { credit: "default", amount: 5 },
label: "workforce expansion",
},
},
},
estimate: ({ data }) => ({
counts: {
result: data.input.body.pageSize,
workforce: data.input.queryParams.expand?.includes("workforce")
? data.input.body.pageSize
: 0,
},
}),
evidence: ({ data, utils }) => {
const delivered = utils.json.len(data.output, "$.items");
const expansion = data.input.queryParams?.expand;
return {
counts: {
// Only the base line has the vendor minimum; expansions
// count delivered records, never totalItems or pageSize.
result: Math.max(1, delivered),
workforce: Array.isArray(expansion) &&
expansion.includes("workforce")
? delivered
: 0,
},
};
},
},
});
Loading