From 9f6673e63f2e909a09de6eae442e03321531e85a Mon Sep 17 00:00:00 2001 From: Waleed Latif Date: Tue, 19 May 2026 11:10:56 -0700 Subject: [PATCH 1/3] feat(wiza): add Wiza integration for B2B prospect enrichment and search --- apps/docs/components/icons.tsx | 11 + apps/docs/components/ui/icon-mapping.ts | 2 + .../content/docs/en/tools/google_docs.mdx | 3 +- apps/docs/content/docs/en/tools/meta.json | 1 + apps/docs/content/docs/en/tools/wiza.mdx | 251 +++++++++++ .../integrations/data/icon-mapping.ts | 2 + .../integrations/data/integrations.json | 41 +- apps/sim/blocks/blocks/wiza.ts | 425 ++++++++++++++++++ apps/sim/blocks/registry.ts | 2 + apps/sim/components/icons.tsx | 11 + apps/sim/tools/registry.ts | 12 + apps/sim/tools/wiza/company_enrichment.ts | 142 ++++++ apps/sim/tools/wiza/get_credits.ts | 62 +++ apps/sim/tools/wiza/get_individual_reveal.ts | 163 +++++++ apps/sim/tools/wiza/index.ts | 6 + apps/sim/tools/wiza/prospect_search.ts | 239 ++++++++++ .../sim/tools/wiza/start_individual_reveal.ts | 142 ++++++ apps/sim/tools/wiza/types.ts | 178 ++++++++ 18 files changed, 1691 insertions(+), 2 deletions(-) create mode 100644 apps/docs/content/docs/en/tools/wiza.mdx create mode 100644 apps/sim/blocks/blocks/wiza.ts create mode 100644 apps/sim/tools/wiza/company_enrichment.ts create mode 100644 apps/sim/tools/wiza/get_credits.ts create mode 100644 apps/sim/tools/wiza/get_individual_reveal.ts create mode 100644 apps/sim/tools/wiza/index.ts create mode 100644 apps/sim/tools/wiza/prospect_search.ts create mode 100644 apps/sim/tools/wiza/start_individual_reveal.ts create mode 100644 apps/sim/tools/wiza/types.ts diff --git a/apps/docs/components/icons.tsx b/apps/docs/components/icons.tsx index 81dbe58d948..6508cb8bbb5 100644 --- a/apps/docs/components/icons.tsx +++ b/apps/docs/components/icons.tsx @@ -6950,3 +6950,14 @@ export function SnowflakeIcon(props: SVGProps) { ) } + +export function WizaIcon(props: SVGProps) { + return ( + + + + ) +} diff --git a/apps/docs/components/ui/icon-mapping.ts b/apps/docs/components/ui/icon-mapping.ts index dd5ab47b8cb..312eb8893ae 100644 --- a/apps/docs/components/ui/icon-mapping.ts +++ b/apps/docs/components/ui/icon-mapping.ts @@ -197,6 +197,7 @@ import { WebflowIcon, WhatsAppIcon, WikipediaIcon, + WizaIcon, WordpressIcon, WorkdayIcon, xIcon, @@ -426,6 +427,7 @@ export const blockTypeToIconMap: Record = { webflow: WebflowIcon, whatsapp: WhatsAppIcon, wikipedia: WikipediaIcon, + wiza: WizaIcon, wordpress: WordpressIcon, workday: WorkdayIcon, x: xIcon, diff --git a/apps/docs/content/docs/en/tools/google_docs.mdx b/apps/docs/content/docs/en/tools/google_docs.mdx index 35dbd4eae8d..069c91f8f6c 100644 --- a/apps/docs/content/docs/en/tools/google_docs.mdx +++ b/apps/docs/content/docs/en/tools/google_docs.mdx @@ -56,7 +56,7 @@ Read content from a Google Docs document ### `google_docs_write` -Write or update content in a Google Docs document +Append content to a Google Docs document. Content is inserted literally; Markdown is not interpreted. For formatted output from Markdown, use the Create operation with the markdown toggle enabled. #### Input @@ -88,6 +88,7 @@ Create a new Google Docs document | `content` | string | No | The content of the document to create | | `folderSelector` | string | No | Google Drive folder ID to create the document in \(e.g., 1ABCxyz...\) | | `folderId` | string | No | The ID of the folder to create the document in \(internal use\) | +| `markdown` | boolean | No | When true, content is interpreted as Markdown and converted to formatted Google Docs content \(headings, bold/italic, lists, tables, links, code blocks, blockquotes\). Default: false \(content inserted as plain text\). | #### Output diff --git a/apps/docs/content/docs/en/tools/meta.json b/apps/docs/content/docs/en/tools/meta.json index 5f04f102bb8..5448fe6407b 100644 --- a/apps/docs/content/docs/en/tools/meta.json +++ b/apps/docs/content/docs/en/tools/meta.json @@ -197,6 +197,7 @@ "webflow", "whatsapp", "wikipedia", + "wiza", "wordpress", "workday", "x", diff --git a/apps/docs/content/docs/en/tools/wiza.mdx b/apps/docs/content/docs/en/tools/wiza.mdx new file mode 100644 index 00000000000..e8d775b07a1 --- /dev/null +++ b/apps/docs/content/docs/en/tools/wiza.mdx @@ -0,0 +1,251 @@ +--- +title: Wiza +description: Find, enrich, and verify B2B contact data with Wiza +--- + +import { BlockInfoCard } from "@/components/ui/block-info-card" + + + +{/* MANUAL-CONTENT-START:intro */} +[Wiza](https://wiza.co/) is a B2B contact data platform that finds and verifies emails and phone numbers for sales, recruiting, and marketing teams. Wiza pairs a global prospect database with real-time enrichment, so the data you act on stays accurate and deliverable. + +With Wiza, you can: + +- **Search a global prospect database**: Find people using person, company, and financial filters +- **Enrich companies**: Resolve firmographic data from a name, domain, or LinkedIn URL +- **Reveal individual contacts**: Get verified work emails, personal emails, and mobile phone numbers +- **Track credit usage**: Check remaining email, phone, export, and API credits at any time + +In Sim, the Wiza integration lets your agents drive prospecting and enrichment workflows end-to-end: + +- **Prospect Search**: Use `wiza_prospect_search` to discover prospects matching detailed filters. +- **Company Enrichment**: Use `wiza_company_enrichment` to enrich a company from name, domain, LinkedIn ID, or LinkedIn slug. +- **Start Individual Reveal**: Use `wiza_start_individual_reveal` to begin enrichment for a contact via LinkedIn URL, name + company, or email — with configurable enrichment depth (`none`, `partial`, `phone`, `full`). +- **Get Individual Reveal**: Use `wiza_get_individual_reveal` to poll a reveal and retrieve verified emails, phones, and full company context. +- **Get Credits**: Use `wiza_get_credits` to monitor remaining credit balances before scaling up runs. + +Individual reveals are asynchronous: start a reveal, then poll `wiza_get_individual_reveal` until `is_complete` is `true`. Combine these tools to build agents that source, enrich, and qualify leads on demand — without leaving your workflow. +{/* MANUAL-CONTENT-END */} + + +## Usage Instructions + +{/* MANUAL-CONTENT-START:usage */} +### Wiza API Key Setup + +Wiza authenticates via API key. To get yours: + +1. Log in to your [Wiza account](https://app.wiza.co/). +2. Open **Settings → API** and generate a new API key (or copy an existing one). +3. In Sim, open the Wiza block and paste the key into the **Wiza API Key** field. + +The same key is used for every Wiza operation. Wiza enforces a rate limit of 30 requests per minute (43,200 per day) per key. + +### Individual Reveals Are Asynchronous + +`wiza_start_individual_reveal` returns immediately with a reveal `id` and a `status` of `queued` or `resolving`. To retrieve the enriched contact data, call `wiza_get_individual_reveal` with that `id` and check `is_complete`. Possible statuses are `queued`, `resolving`, `finished`, and `failed`. + +For real-time delivery without polling, pass a `callback_url` when starting the reveal — Wiza will POST the completed payload to that URL. + +### Enrichment Levels and Credits + +When starting an individual reveal, choose the enrichment level that matches the data you need: + +- **`none`** — Identity only, no contact info (no credit spend) +- **`partial`** — Verified work email (email credits) +- **`phone`** — Mobile phone (phone credits) +- **`full`** — Email + phone (email + phone credits) + +Use `wiza_get_credits` to monitor remaining email, phone, export, and API credits before running large batches. +{/* MANUAL-CONTENT-END */} + + +Integrates Wiza into the workflow. Search prospects, enrich companies, reveal verified emails and phone numbers for individuals, and check your account credit balance. + + + +## Tools + +### `wiza_prospect_search` + +Search Wiza + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `apiKey` | string | Yes | Wiza API key | +| `size` | number | No | Number of sample profiles to return \(0-30, default 0\) | +| `filters` | object | No | Full filters object \(overrides individual filter params if provided\) | +| `first_name` | array | No | Exact first names to match \(e.g., \["John", "Jane"\]\) | +| `last_name` | array | No | Exact last names to match | +| `job_title` | array | No | Job titles to include/exclude \(e.g., \[\{"v":"CEO","s":"i"\},\{"v":"CTO","s":"e"\}\]\) | +| `job_title_level` | array | No | Seniority levels \(e.g., \["cxo", "director", "manager"\]\) | +| `job_role` | array | No | Job role categories \(e.g., \["sales", "engineering", "marketing"\]\) | +| `job_sub_role` | array | No | Detailed role categories \(e.g., \["software", "product"\]\) | +| `location` | array | No | Person's location filters \(city/state/country with include/exclude\) | +| `skill` | array | No | Professional skills \(e.g., \["python", "marketing"\]\) | +| `school` | array | No | Educational institutions | +| `major` | array | No | Field of study | +| `linkedin_slug` | array | No | LinkedIn profile slugs | +| `job_company` | array | No | Current company filters \(include/exclude\) | +| `past_company` | array | No | Past company filters | +| `company_location` | array | No | Company HQ location filters | +| `company_industry` | array | No | Company industry filters \(include/exclude\) | +| `company_size` | array | No | Company headcount brackets \(e.g., \["1-10", "11-50", "51-200"\]\) | +| `company_type` | array | No | Company type \(e.g., \["private", "public", "educational"\]\) | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `total` | number | Total number of matching prospects | +| `profiles` | array | Sample profiles matching the filter criteria | + +### `wiza_company_enrichment` + +Enrich a company by name, domain, LinkedIn ID, or LinkedIn slug with detailed firmographic data + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `apiKey` | string | Yes | Wiza API key | +| `company_name` | string | No | Company name \(e.g., "Wiza"\) | +| `company_domain` | string | No | Company domain \(e.g., "wiza.co"\) | +| `company_linkedin_id` | string | No | Company LinkedIn ID | +| `company_linkedin_slug` | string | No | Company LinkedIn slug from the URL | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `company_name` | string | Company name | +| `company_domain` | string | Company domain | +| `domain` | string | Domain | +| `company_industry` | string | Industry | +| `company_size` | number | Employee count | +| `company_size_range` | string | Headcount range | +| `company_founded` | number | Year founded | +| `company_revenue_range` | string | Revenue range | +| `company_funding` | string | Total funding | +| `company_type` | string | Company type | +| `company_description` | string | Description | +| `company_ticker` | string | Stock ticker | +| `company_last_funding_round` | string | Last funding round | +| `company_last_funding_amount` | string | Last funding amount | +| `company_last_funding_at` | string | Last funding date | +| `company_location` | string | Full location string | +| `company_twitter` | string | Twitter URL | +| `company_facebook` | string | Facebook URL | +| `company_linkedin` | string | LinkedIn URL | +| `company_linkedin_id` | string | LinkedIn ID | +| `company_street` | string | Street address | +| `company_locality` | string | City | +| `company_region` | string | State/region | +| `company_postal_code` | string | Postal code | +| `company_country` | string | Country | +| `credits` | json | Remaining API credits | + +### `wiza_start_individual_reveal` + +Start an individual reveal to enrich a contact via LinkedIn URL, name+company, or email + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `apiKey` | string | Yes | Wiza API key | +| `enrichment_level` | string | Yes | Enrichment depth: none, partial, phone, or full | +| `profile_url` | string | No | LinkedIn profile URL \(e.g., https://linkedin.com/in/johndoe\) | +| `full_name` | string | No | Full name \(used with company or domain\) | +| `company` | string | No | Company name \(used with full_name\) | +| `domain` | string | No | Company domain \(used with full_name\) | +| `email` | string | No | Email address \(use alone or with other identifiers\) | +| `accept_work` | boolean | No | Whether to accept work emails \(email_options\) | +| `accept_personal` | boolean | No | Whether to accept personal emails \(email_options\) | +| `callback_url` | string | No | Optional URL to receive a callback with the reveal update | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `id` | number | Individual reveal ID \(use with Get Individual Reveal\) | +| `status` | string | Reveal status: queued, resolving, finished, or failed | +| `is_complete` | boolean | Whether the reveal has completed | + +### `wiza_get_individual_reveal` + +Retrieve the status and enriched data for an individual reveal by ID + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `apiKey` | string | Yes | Wiza API key | +| `id` | string | Yes | Individual reveal ID returned from Start Individual Reveal | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `id` | number | Reveal ID | +| `status` | string | queued \| resolving \| finished \| failed | +| `is_complete` | boolean | Whether the reveal has completed | +| `name` | string | Full name | +| `company` | string | Company name | +| `enrichment_level` | string | Enrichment level used | +| `linkedin_profile_url` | string | LinkedIn URL | +| `title` | string | Job title | +| `location` | string | Location | +| `email` | string | Primary email | +| `email_type` | string | Email type | +| `email_status` | string | valid \| risky \| unfound | +| `emails` | array | All emails found | +| `mobile_phone` | string | Mobile phone | +| `phone_number` | string | Direct/office phone | +| `phone_status` | string | found \| unfound | +| `phones` | array | All phones found | +| `company_size` | number | Employee count | +| `company_size_range` | string | Headcount range | +| `company_type` | string | Company type | +| `company_domain` | string | Company domain | +| `company_locality` | string | City | +| `company_region` | string | State/region | +| `company_country` | string | Country | +| `company_street` | string | Street | +| `company_postal_code` | string | Postal code | +| `company_founded` | number | Year founded | +| `company_funding` | string | Funding total | +| `company_revenue` | string | Revenue | +| `company_industry` | string | Industry | +| `company_subindustry` | string | Subindustry | +| `company_linkedin` | string | Company LinkedIn URL | +| `company_location` | string | Full company location | +| `company_description` | string | Company description | +| `credits` | json | Remaining credits balance | + +### `wiza_get_credits` + +Retrieve the remaining credits on your Wiza account + +#### Input + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `apiKey` | string | Yes | Wiza API key | + +#### Output + +| Parameter | Type | Description | +| --------- | ---- | ----------- | +| `email_credits` | json | Remaining email credits \(number or "unlimited"\) | +| `phone_credits` | json | Remaining phone credits \(number or "unlimited"\) | +| `export_credits` | number | Remaining export credits | +| `api_credits` | number | Remaining API credits | + + diff --git a/apps/sim/app/(landing)/integrations/data/icon-mapping.ts b/apps/sim/app/(landing)/integrations/data/icon-mapping.ts index 64fe8a8556d..ab6f6b1831c 100644 --- a/apps/sim/app/(landing)/integrations/data/icon-mapping.ts +++ b/apps/sim/app/(landing)/integrations/data/icon-mapping.ts @@ -197,6 +197,7 @@ import { WebflowIcon, WhatsAppIcon, WikipediaIcon, + WizaIcon, WordpressIcon, WorkdayIcon, xIcon, @@ -403,6 +404,7 @@ export const blockTypeToIconMap: Record = { webflow: WebflowIcon, whatsapp: WhatsAppIcon, wikipedia: WikipediaIcon, + wiza: WizaIcon, wordpress: WordpressIcon, workday: WorkdayIcon, x: xIcon, diff --git a/apps/sim/app/(landing)/integrations/data/integrations.json b/apps/sim/app/(landing)/integrations/data/integrations.json index d67628c4ab8..b3d8b3bc4fc 100644 --- a/apps/sim/app/(landing)/integrations/data/integrations.json +++ b/apps/sim/app/(landing)/integrations/data/integrations.json @@ -5202,7 +5202,7 @@ }, { "name": "Write to Document", - "description": "Write or update content in a Google Docs document" + "description": "Append content to a Google Docs document. Content is inserted literally; Markdown is not interpreted. For formatted output from Markdown, use the Create operation with the markdown toggle enabled." }, { "name": "Create Document", @@ -14212,6 +14212,45 @@ "integrationTypes": ["search", "documents"], "tags": ["knowledge-base", "web-scraping"] }, + { + "type": "wiza", + "slug": "wiza", + "name": "Wiza", + "description": "Find, enrich, and verify B2B contact data with Wiza", + "longDescription": "Integrates Wiza into the workflow. Search prospects, enrich companies, reveal verified emails and phone numbers for individuals, and check your account credit balance.", + "bgColor": "#9284BC", + "iconName": "WizaIcon", + "docsUrl": "https://docs.sim.ai/tools/wiza", + "operations": [ + { + "name": "Prospect Search", + "description": "Search Wiza" + }, + { + "name": "Company Enrichment", + "description": "Enrich a company by name, domain, LinkedIn ID, or LinkedIn slug with detailed firmographic data" + }, + { + "name": "Start Individual Reveal", + "description": "Start an individual reveal to enrich a contact via LinkedIn URL, name+company, or email" + }, + { + "name": "Get Individual Reveal", + "description": "Retrieve the status and enriched data for an individual reveal by ID" + }, + { + "name": "Get Credits", + "description": "Retrieve the remaining credits on your Wiza account" + } + ], + "operationCount": 5, + "triggers": [], + "triggerCount": 0, + "authType": "api-key", + "category": "tools", + "integrationTypes": ["sales"], + "tags": ["enrichment", "sales-engagement"] + }, { "type": "wordpress", "slug": "wordpress", diff --git a/apps/sim/blocks/blocks/wiza.ts b/apps/sim/blocks/blocks/wiza.ts new file mode 100644 index 00000000000..04fc870cc85 --- /dev/null +++ b/apps/sim/blocks/blocks/wiza.ts @@ -0,0 +1,425 @@ +import { WizaIcon } from '@/components/icons' +import type { BlockConfig } from '@/blocks/types' +import { AuthMode, IntegrationType } from '@/blocks/types' +import type { WizaResponse } from '@/tools/wiza/types' + +export const WizaBlock: BlockConfig = { + type: 'wiza', + name: 'Wiza', + description: 'Find, enrich, and verify B2B contact data with Wiza', + authMode: AuthMode.ApiKey, + longDescription: + 'Integrates Wiza into the workflow. Search prospects, enrich companies, reveal verified emails and phone numbers for individuals, and check your account credit balance.', + docsLink: 'https://docs.sim.ai/tools/wiza', + category: 'tools', + integrationType: IntegrationType.Sales, + tags: ['enrichment', 'sales-engagement'], + bgColor: '#9284BC', + icon: WizaIcon, + subBlocks: [ + { + id: 'operation', + title: 'Operation', + type: 'dropdown', + options: [ + { label: 'Prospect Search', id: 'prospect_search' }, + { label: 'Company Enrichment', id: 'company_enrichment' }, + { label: 'Start Individual Reveal', id: 'start_individual_reveal' }, + { label: 'Get Individual Reveal', id: 'get_individual_reveal' }, + { label: 'Get Credits', id: 'get_credits' }, + ], + value: () => 'prospect_search', + }, + { + id: 'apiKey', + title: 'Wiza API Key', + type: 'short-input', + placeholder: 'Enter your Wiza API key', + password: true, + required: true, + }, + + // Prospect Search + { + id: 'size', + title: 'Sample Size', + type: 'short-input', + placeholder: '0-30 (default 0, returns total only)', + condition: { field: 'operation', value: 'prospect_search' }, + }, + { + id: 'job_title', + title: 'Job Titles', + type: 'code', + placeholder: '[{"v":"CEO","s":"i"},{"v":"Founder","s":"i"}]', + condition: { field: 'operation', value: 'prospect_search' }, + mode: 'advanced', + }, + { + id: 'job_title_level', + title: 'Job Title Levels', + type: 'code', + placeholder: '["cxo", "director", "manager"]', + condition: { field: 'operation', value: 'prospect_search' }, + mode: 'advanced', + }, + { + id: 'job_role', + title: 'Job Roles', + type: 'code', + placeholder: '["sales", "engineering", "marketing"]', + condition: { field: 'operation', value: 'prospect_search' }, + mode: 'advanced', + }, + { + id: 'job_sub_role', + title: 'Job Sub-Roles', + type: 'code', + placeholder: '["software", "product"]', + condition: { field: 'operation', value: 'prospect_search' }, + mode: 'advanced', + }, + { + id: 'first_name', + title: 'First Names', + type: 'code', + placeholder: '["John", "Jane"]', + condition: { field: 'operation', value: 'prospect_search' }, + mode: 'advanced', + }, + { + id: 'last_name', + title: 'Last Names', + type: 'code', + placeholder: '["Smith", "Doe"]', + condition: { field: 'operation', value: 'prospect_search' }, + mode: 'advanced', + }, + { + id: 'location', + title: 'Person Locations', + type: 'code', + placeholder: '[{"v":{"country":"united states"},"b":"city","s":"i"}]', + condition: { field: 'operation', value: 'prospect_search' }, + mode: 'advanced', + }, + { + id: 'skill', + title: 'Skills', + type: 'code', + placeholder: '["python", "marketing"]', + condition: { field: 'operation', value: 'prospect_search' }, + mode: 'advanced', + }, + { + id: 'school', + title: 'Schools', + type: 'code', + placeholder: '["stanford university"]', + condition: { field: 'operation', value: 'prospect_search' }, + mode: 'advanced', + }, + { + id: 'major', + title: 'Majors', + type: 'code', + placeholder: '["computer science"]', + condition: { field: 'operation', value: 'prospect_search' }, + mode: 'advanced', + }, + { + id: 'linkedin_slug', + title: 'LinkedIn Slugs', + type: 'code', + placeholder: '["john-doe-123"]', + condition: { field: 'operation', value: 'prospect_search' }, + mode: 'advanced', + }, + { + id: 'job_company', + title: 'Current Companies', + type: 'code', + placeholder: '[{"v":"wiza","s":"i"}]', + condition: { field: 'operation', value: 'prospect_search' }, + mode: 'advanced', + }, + { + id: 'past_company', + title: 'Past Companies', + type: 'code', + placeholder: '[{"v":"google","s":"i"}]', + condition: { field: 'operation', value: 'prospect_search' }, + mode: 'advanced', + }, + { + id: 'company_location', + title: 'Company Locations', + type: 'code', + placeholder: '[{"v":{"country":"canada"},"b":"country","s":"i"}]', + condition: { field: 'operation', value: 'prospect_search' }, + mode: 'advanced', + }, + { + id: 'company_industry', + title: 'Company Industries', + type: 'code', + placeholder: '[{"v":"computer software","s":"i"}]', + condition: { field: 'operation', value: 'prospect_search' }, + mode: 'advanced', + }, + { + id: 'company_size', + title: 'Company Sizes', + type: 'code', + placeholder: '["11-50", "51-200"]', + condition: { field: 'operation', value: 'prospect_search' }, + mode: 'advanced', + }, + { + id: 'company_type', + title: 'Company Types', + type: 'code', + placeholder: '["private", "public"]', + condition: { field: 'operation', value: 'prospect_search' }, + mode: 'advanced', + }, + { + id: 'filters', + title: 'Full Filters Object (overrides above)', + type: 'code', + placeholder: '{"job_title":[{"v":"CEO","s":"i"}], "company_size":["11-50"]}', + condition: { field: 'operation', value: 'prospect_search' }, + mode: 'advanced', + }, + + // Company Enrichment + { + id: 'company_name', + title: 'Company Name', + type: 'short-input', + placeholder: 'Wiza', + condition: { field: 'operation', value: 'company_enrichment' }, + }, + { + id: 'company_domain', + title: 'Company Domain', + type: 'short-input', + placeholder: 'wiza.co', + condition: { field: 'operation', value: 'company_enrichment' }, + }, + { + id: 'company_linkedin_id', + title: 'Company LinkedIn ID', + type: 'short-input', + placeholder: '11272782', + condition: { field: 'operation', value: 'company_enrichment' }, + mode: 'advanced', + }, + { + id: 'company_linkedin_slug', + title: 'Company LinkedIn Slug', + type: 'short-input', + placeholder: 'wizaco', + condition: { field: 'operation', value: 'company_enrichment' }, + mode: 'advanced', + }, + + // Start Individual Reveal + { + id: 'enrichment_level', + title: 'Enrichment Level', + type: 'dropdown', + options: [ + { label: 'None', id: 'none' }, + { label: 'Partial', id: 'partial' }, + { label: 'Phone', id: 'phone' }, + { label: 'Full', id: 'full' }, + ], + value: () => 'full', + condition: { field: 'operation', value: 'start_individual_reveal' }, + required: { field: 'operation', value: 'start_individual_reveal' }, + }, + { + id: 'profile_url', + title: 'LinkedIn Profile URL', + type: 'short-input', + placeholder: 'https://linkedin.com/in/johndoe', + condition: { field: 'operation', value: 'start_individual_reveal' }, + }, + { + id: 'full_name', + title: 'Full Name', + type: 'short-input', + placeholder: 'John Doe', + condition: { field: 'operation', value: 'start_individual_reveal' }, + }, + { + id: 'company', + title: 'Company', + type: 'short-input', + placeholder: 'Wiza', + condition: { field: 'operation', value: 'start_individual_reveal' }, + }, + { + id: 'domain', + title: 'Company Domain', + type: 'short-input', + placeholder: 'wiza.co', + condition: { field: 'operation', value: 'start_individual_reveal' }, + }, + { + id: 'email', + title: 'Email', + type: 'short-input', + placeholder: 'john@wiza.co', + condition: { field: 'operation', value: 'start_individual_reveal' }, + }, + { + id: 'accept_work', + title: 'Accept Work Emails', + type: 'switch', + condition: { field: 'operation', value: 'start_individual_reveal' }, + mode: 'advanced', + }, + { + id: 'accept_personal', + title: 'Accept Personal Emails', + type: 'switch', + condition: { field: 'operation', value: 'start_individual_reveal' }, + mode: 'advanced', + }, + { + id: 'callback_url', + title: 'Callback URL', + type: 'short-input', + placeholder: 'https://example.com/wiza-callback', + condition: { field: 'operation', value: 'start_individual_reveal' }, + mode: 'advanced', + }, + + // Get Individual Reveal + { + id: 'id', + title: 'Reveal ID', + type: 'short-input', + placeholder: 'Reveal ID returned from Start Individual Reveal', + condition: { field: 'operation', value: 'get_individual_reveal' }, + required: { field: 'operation', value: 'get_individual_reveal' }, + }, + ], + + tools: { + access: [ + 'wiza_prospect_search', + 'wiza_company_enrichment', + 'wiza_start_individual_reveal', + 'wiza_get_individual_reveal', + 'wiza_get_credits', + ], + config: { + tool: (params) => { + switch (params.operation) { + case 'prospect_search': + return 'wiza_prospect_search' + case 'company_enrichment': + return 'wiza_company_enrichment' + case 'start_individual_reveal': + return 'wiza_start_individual_reveal' + case 'get_individual_reveal': + return 'wiza_get_individual_reveal' + case 'get_credits': + return 'wiza_get_credits' + default: + throw new Error(`Invalid Wiza operation: ${params.operation}`) + } + }, + params: (params) => { + const parsed: Record = { ...params } + + const parseJsonField = (field: string) => { + const value = parsed[field] + if (typeof value === 'string' && value.trim() !== '') { + try { + parsed[field] = JSON.parse(value) + } catch { + // Leave as-is if not valid JSON; tool may still accept the string + } + } + } + + for (const field of [ + 'filters', + 'first_name', + 'last_name', + 'job_title', + 'job_title_level', + 'job_role', + 'job_sub_role', + 'location', + 'skill', + 'school', + 'major', + 'linkedin_slug', + 'job_company', + 'past_company', + 'company_location', + 'company_industry', + 'company_size', + 'company_type', + ]) { + parseJsonField(field) + } + + if (typeof parsed.size === 'string' && parsed.size.trim() !== '') { + const n = Number(parsed.size) + if (!Number.isNaN(n)) parsed.size = n + } + + return parsed + }, + }, + }, + + inputs: { + apiKey: { type: 'string', description: 'Wiza API key' }, + operation: { type: 'string', description: 'Operation to perform' }, + size: { type: 'number', description: 'Sample size for prospect search (0-30)' }, + filters: { type: 'json', description: 'Full filters object for prospect search' }, + first_name: { type: 'json', description: 'First name filter array' }, + last_name: { type: 'json', description: 'Last name filter array' }, + job_title: { type: 'json', description: 'Job title filter array' }, + job_title_level: { type: 'json', description: 'Job title level filter' }, + job_role: { type: 'json', description: 'Job role filter' }, + job_sub_role: { type: 'json', description: 'Job sub-role filter' }, + location: { type: 'json', description: 'Person location filter' }, + skill: { type: 'json', description: 'Skill filter' }, + school: { type: 'json', description: 'School filter' }, + major: { type: 'json', description: 'Major filter' }, + linkedin_slug: { type: 'json', description: 'LinkedIn slug filter' }, + job_company: { type: 'json', description: 'Current company filter' }, + past_company: { type: 'json', description: 'Past company filter' }, + company_location: { type: 'json', description: 'Company location filter' }, + company_industry: { type: 'json', description: 'Company industry filter' }, + company_size: { type: 'json', description: 'Company size filter' }, + company_type: { type: 'json', description: 'Company type filter' }, + company_name: { type: 'string', description: 'Company name' }, + company_domain: { type: 'string', description: 'Company domain' }, + company_linkedin_id: { type: 'string', description: 'Company LinkedIn ID' }, + company_linkedin_slug: { type: 'string', description: 'Company LinkedIn slug' }, + enrichment_level: { type: 'string', description: 'Enrichment level for individual reveal' }, + profile_url: { type: 'string', description: 'LinkedIn profile URL' }, + full_name: { type: 'string', description: 'Full name' }, + company: { type: 'string', description: 'Company' }, + domain: { type: 'string', description: 'Domain' }, + email: { type: 'string', description: 'Email address' }, + accept_work: { type: 'boolean', description: 'Whether to accept work emails' }, + accept_personal: { type: 'boolean', description: 'Whether to accept personal emails' }, + callback_url: { type: 'string', description: 'Callback URL' }, + id: { type: 'string', description: 'Individual reveal ID' }, + }, + + outputs: { + success: { type: 'boolean', description: 'Whether the operation was successful' }, + output: { type: 'json', description: 'Output data from the Wiza operation' }, + }, +} diff --git a/apps/sim/blocks/registry.ts b/apps/sim/blocks/registry.ts index 268e010543f..2e008d00edb 100644 --- a/apps/sim/blocks/registry.ts +++ b/apps/sim/blocks/registry.ts @@ -225,6 +225,7 @@ import { WebflowBlock } from '@/blocks/blocks/webflow' import { WebhookRequestBlock } from '@/blocks/blocks/webhook_request' import { WhatsAppBlock } from '@/blocks/blocks/whatsapp' import { WikipediaBlock } from '@/blocks/blocks/wikipedia' +import { WizaBlock } from '@/blocks/blocks/wiza' import { WordPressBlock } from '@/blocks/blocks/wordpress' import { WorkdayBlock } from '@/blocks/blocks/workday' import { WorkflowBlock } from '@/blocks/blocks/workflow' @@ -488,6 +489,7 @@ export const registry: Record = { webhook_request: WebhookRequestBlock, whatsapp: WhatsAppBlock, wikipedia: WikipediaBlock, + wiza: WizaBlock, wordpress: WordPressBlock, workday: WorkdayBlock, workflow: WorkflowBlock, diff --git a/apps/sim/components/icons.tsx b/apps/sim/components/icons.tsx index 81dbe58d948..6508cb8bbb5 100644 --- a/apps/sim/components/icons.tsx +++ b/apps/sim/components/icons.tsx @@ -6950,3 +6950,14 @@ export function SnowflakeIcon(props: SVGProps) { ) } + +export function WizaIcon(props: SVGProps) { + return ( + + + + ) +} diff --git a/apps/sim/tools/registry.ts b/apps/sim/tools/registry.ts index dad88d1a940..b65d6bda699 100644 --- a/apps/sim/tools/registry.ts +++ b/apps/sim/tools/registry.ts @@ -2937,6 +2937,13 @@ import { wikipediaRandomPageTool, wikipediaSearchTool, } from '@/tools/wikipedia' +import { + wizaCompanyEnrichmentTool, + wizaGetCreditsTool, + wizaGetIndividualRevealTool, + wizaProspectSearchTool, + wizaStartIndividualRevealTool, +} from '@/tools/wiza' import { wordpressCreateCategoryTool, wordpressCreateCommentTool, @@ -5228,6 +5235,11 @@ export const tools: Record = { wikipedia_search: wikipediaSearchTool, wikipedia_content: wikipediaPageContentTool, wikipedia_random: wikipediaRandomPageTool, + wiza_company_enrichment: wizaCompanyEnrichmentTool, + wiza_get_credits: wizaGetCreditsTool, + wiza_get_individual_reveal: wizaGetIndividualRevealTool, + wiza_prospect_search: wizaProspectSearchTool, + wiza_start_individual_reveal: wizaStartIndividualRevealTool, wordpress_create_post: wordpressCreatePostTool, wordpress_update_post: wordpressUpdatePostTool, wordpress_delete_post: wordpressDeletePostTool, diff --git a/apps/sim/tools/wiza/company_enrichment.ts b/apps/sim/tools/wiza/company_enrichment.ts new file mode 100644 index 00000000000..75201dd694c --- /dev/null +++ b/apps/sim/tools/wiza/company_enrichment.ts @@ -0,0 +1,142 @@ +import type { ToolConfig } from '@/tools/types' +import type { WizaCompanyEnrichmentParams, WizaCompanyEnrichmentResponse } from '@/tools/wiza/types' + +export const wizaCompanyEnrichmentTool: ToolConfig< + WizaCompanyEnrichmentParams, + WizaCompanyEnrichmentResponse +> = { + id: 'wiza_company_enrichment', + name: 'Wiza Company Enrichment', + description: + 'Enrich a company by name, domain, LinkedIn ID, or LinkedIn slug with detailed firmographic data', + version: '1.0.0', + + params: { + apiKey: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Wiza API key', + }, + company_name: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Company name (e.g., "Wiza")', + }, + company_domain: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Company domain (e.g., "wiza.co")', + }, + company_linkedin_id: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Company LinkedIn ID', + }, + company_linkedin_slug: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Company LinkedIn slug from the URL', + }, + }, + + request: { + url: 'https://wiza.co/api/company_enrichments', + method: 'POST', + headers: (params: WizaCompanyEnrichmentParams) => ({ + Authorization: `Bearer ${params.apiKey}`, + 'Content-Type': 'application/json', + }), + body: (params: WizaCompanyEnrichmentParams) => { + const body: Record = {} + if (params.company_name) body.company_name = params.company_name + if (params.company_domain) body.company_domain = params.company_domain + if (params.company_linkedin_id) body.company_linkedin_id = params.company_linkedin_id + if (params.company_linkedin_slug) body.company_linkedin_slug = params.company_linkedin_slug + return body + }, + }, + + transformResponse: async (response: Response) => { + if (!response.ok) { + const errorText = await response.text() + throw new Error(`Wiza API error: ${response.status} - ${errorText}`) + } + + const json = await response.json() + const d = json.data ?? {} + + return { + success: true, + output: { + company_name: d.company_name ?? null, + company_domain: d.company_domain ?? null, + domain: d.domain ?? null, + company_industry: d.company_industry ?? null, + company_size: d.company_size ?? null, + company_size_range: d.company_size_range ?? null, + company_founded: d.company_founded ?? null, + company_revenue_range: d.company_revenue_range ?? null, + company_funding: d.company_funding ?? null, + company_type: d.company_type ?? null, + company_description: d.company_description ?? null, + company_ticker: d.company_ticker ?? null, + company_last_funding_round: d.company_last_funding_round ?? null, + company_last_funding_amount: d.company_last_funding_amount ?? null, + company_last_funding_at: d.company_last_funding_at ?? null, + company_location: d.company_location ?? null, + company_twitter: d.company_twitter ?? null, + company_facebook: d.company_facebook ?? null, + company_linkedin: d.company_linkedin ?? null, + company_linkedin_id: d.company_linkedin_id ?? null, + company_street: d.company_street ?? null, + company_locality: d.company_locality ?? null, + company_region: d.company_region ?? null, + company_postal_code: d.company_postal_code ?? null, + company_country: d.company_country ?? null, + credits: d.credits ?? null, + }, + } + }, + + outputs: { + company_name: { type: 'string', description: 'Company name', optional: true }, + company_domain: { type: 'string', description: 'Company domain', optional: true }, + domain: { type: 'string', description: 'Domain', optional: true }, + company_industry: { type: 'string', description: 'Industry', optional: true }, + company_size: { type: 'number', description: 'Employee count', optional: true }, + company_size_range: { type: 'string', description: 'Headcount range', optional: true }, + company_founded: { type: 'number', description: 'Year founded', optional: true }, + company_revenue_range: { type: 'string', description: 'Revenue range', optional: true }, + company_funding: { type: 'string', description: 'Total funding', optional: true }, + company_type: { type: 'string', description: 'Company type', optional: true }, + company_description: { type: 'string', description: 'Description', optional: true }, + company_ticker: { type: 'string', description: 'Stock ticker', optional: true }, + company_last_funding_round: { + type: 'string', + description: 'Last funding round', + optional: true, + }, + company_last_funding_amount: { + type: 'string', + description: 'Last funding amount', + optional: true, + }, + company_last_funding_at: { type: 'string', description: 'Last funding date', optional: true }, + company_location: { type: 'string', description: 'Full location string', optional: true }, + company_twitter: { type: 'string', description: 'Twitter URL', optional: true }, + company_facebook: { type: 'string', description: 'Facebook URL', optional: true }, + company_linkedin: { type: 'string', description: 'LinkedIn URL', optional: true }, + company_linkedin_id: { type: 'string', description: 'LinkedIn ID', optional: true }, + company_street: { type: 'string', description: 'Street address', optional: true }, + company_locality: { type: 'string', description: 'City', optional: true }, + company_region: { type: 'string', description: 'State/region', optional: true }, + company_postal_code: { type: 'string', description: 'Postal code', optional: true }, + company_country: { type: 'string', description: 'Country', optional: true }, + credits: { type: 'json', description: 'Remaining API credits', optional: true }, + }, +} diff --git a/apps/sim/tools/wiza/get_credits.ts b/apps/sim/tools/wiza/get_credits.ts new file mode 100644 index 00000000000..3ff8f02c2e7 --- /dev/null +++ b/apps/sim/tools/wiza/get_credits.ts @@ -0,0 +1,62 @@ +import type { ToolConfig } from '@/tools/types' +import type { WizaGetCreditsParams, WizaGetCreditsResponse } from '@/tools/wiza/types' + +export const wizaGetCreditsTool: ToolConfig = { + id: 'wiza_get_credits', + name: 'Wiza Get Credits', + description: 'Retrieve the remaining credits on your Wiza account', + version: '1.0.0', + + params: { + apiKey: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Wiza API key', + }, + }, + + request: { + url: 'https://wiza.co/api/meta/credits', + method: 'GET', + headers: (params: WizaGetCreditsParams) => ({ + Authorization: `Bearer ${params.apiKey}`, + 'Content-Type': 'application/json', + }), + }, + + transformResponse: async (response: Response) => { + if (!response.ok) { + const errorText = await response.text() + throw new Error(`Wiza API error: ${response.status} - ${errorText}`) + } + + const data = await response.json() + const credits = data.credits ?? {} + + return { + success: true, + output: { + email_credits: credits.email_credits ?? null, + phone_credits: credits.phone_credits ?? null, + export_credits: credits.export_credits ?? null, + api_credits: credits.api_credits ?? null, + }, + } + }, + + outputs: { + email_credits: { + type: 'json', + description: 'Remaining email credits (number or "unlimited")', + optional: true, + }, + phone_credits: { + type: 'json', + description: 'Remaining phone credits (number or "unlimited")', + optional: true, + }, + export_credits: { type: 'number', description: 'Remaining export credits', optional: true }, + api_credits: { type: 'number', description: 'Remaining API credits', optional: true }, + }, +} diff --git a/apps/sim/tools/wiza/get_individual_reveal.ts b/apps/sim/tools/wiza/get_individual_reveal.ts new file mode 100644 index 00000000000..bbf13e8dcba --- /dev/null +++ b/apps/sim/tools/wiza/get_individual_reveal.ts @@ -0,0 +1,163 @@ +import type { ToolConfig } from '@/tools/types' +import type { + WizaGetIndividualRevealParams, + WizaGetIndividualRevealResponse, +} from '@/tools/wiza/types' + +export const wizaGetIndividualRevealTool: ToolConfig< + WizaGetIndividualRevealParams, + WizaGetIndividualRevealResponse +> = { + id: 'wiza_get_individual_reveal', + name: 'Wiza Get Individual Reveal', + description: 'Retrieve the status and enriched data for an individual reveal by ID', + version: '1.0.0', + + params: { + apiKey: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Wiza API key', + }, + id: { + type: 'string', + required: true, + visibility: 'user-or-llm', + description: 'Individual reveal ID returned from Start Individual Reveal', + }, + }, + + request: { + url: (params: WizaGetIndividualRevealParams) => + `https://wiza.co/api/individual_reveals/${encodeURIComponent(params.id.trim())}`, + method: 'GET', + headers: (params: WizaGetIndividualRevealParams) => ({ + Authorization: `Bearer ${params.apiKey}`, + 'Content-Type': 'application/json', + }), + }, + + transformResponse: async (response: Response) => { + if (!response.ok) { + const errorText = await response.text() + throw new Error(`Wiza API error: ${response.status} - ${errorText}`) + } + + const json = await response.json() + const d = json.data ?? {} + const emails = Array.isArray(d.emails) ? d.emails : [] + const phones = Array.isArray(d.phones) ? d.phones : [] + + return { + success: true, + output: { + id: d.id ?? null, + status: d.status ?? null, + is_complete: d.is_complete ?? null, + name: d.name ?? null, + company: d.company ?? null, + enrichment_level: d.enrichment_level ?? null, + linkedin_profile_url: d.linkedin_profile_url ?? null, + title: d.title ?? null, + location: d.location ?? null, + email: d.email ?? null, + email_type: d.email_type ?? null, + email_status: d.email_status ?? null, + emails: emails.map((e: Record) => ({ + email: (e.email as string) ?? null, + email_type: (e.email_type as string) ?? null, + email_status: (e.email_status as string) ?? null, + })), + mobile_phone: d.mobile_phone ?? null, + phone_number: d.phone_number ?? null, + phone_status: d.phone_status ?? null, + phones: phones.map((p: Record) => ({ + number: (p.number as string) ?? null, + pretty_number: (p.pretty_number as string) ?? null, + type: (p.type as string) ?? null, + })), + company_size: d.company_size ?? null, + company_size_range: d.company_size_range ?? null, + company_type: d.company_type ?? null, + company_domain: d.company_domain ?? null, + company_locality: d.company_locality ?? null, + company_region: d.company_region ?? null, + company_country: d.company_country ?? null, + company_street: d.company_street ?? null, + company_postal_code: d.company_postal_code ?? null, + company_founded: d.company_founded ?? null, + company_funding: d.company_funding ?? null, + company_revenue: d.company_revenue ?? null, + company_industry: d.company_industry ?? null, + company_subindustry: d.company_subindustry ?? null, + company_linkedin: d.company_linkedin ?? null, + company_location: d.company_location ?? null, + company_description: d.company_description ?? null, + credits: d.credits ?? null, + }, + } + }, + + outputs: { + id: { type: 'number', description: 'Reveal ID' }, + status: { type: 'string', description: 'queued | resolving | finished | failed' }, + is_complete: { type: 'boolean', description: 'Whether the reveal has completed' }, + name: { type: 'string', description: 'Full name', optional: true }, + company: { type: 'string', description: 'Company name', optional: true }, + enrichment_level: { type: 'string', description: 'Enrichment level used', optional: true }, + linkedin_profile_url: { type: 'string', description: 'LinkedIn URL', optional: true }, + title: { type: 'string', description: 'Job title', optional: true }, + location: { type: 'string', description: 'Location', optional: true }, + email: { type: 'string', description: 'Primary email', optional: true }, + email_type: { type: 'string', description: 'Email type', optional: true }, + email_status: { type: 'string', description: 'valid | risky | unfound', optional: true }, + emails: { + type: 'array', + description: 'All emails found', + optional: true, + items: { + type: 'object', + properties: { + email: { type: 'string' }, + email_type: { type: 'string' }, + email_status: { type: 'string' }, + }, + }, + }, + mobile_phone: { type: 'string', description: 'Mobile phone', optional: true }, + phone_number: { type: 'string', description: 'Direct/office phone', optional: true }, + phone_status: { type: 'string', description: 'found | unfound', optional: true }, + phones: { + type: 'array', + description: 'All phones found', + optional: true, + items: { + type: 'object', + properties: { + number: { type: 'string' }, + pretty_number: { type: 'string' }, + type: { type: 'string' }, + }, + }, + }, + company_size: { type: 'number', description: 'Employee count', optional: true }, + company_size_range: { type: 'string', description: 'Headcount range', optional: true }, + company_type: { type: 'string', description: 'Company type', optional: true }, + company_domain: { type: 'string', description: 'Company domain', optional: true }, + company_locality: { type: 'string', description: 'City', optional: true }, + company_region: { type: 'string', description: 'State/region', optional: true }, + company_country: { type: 'string', description: 'Country', optional: true }, + company_street: { type: 'string', description: 'Street', optional: true }, + company_postal_code: { type: 'string', description: 'Postal code', optional: true }, + company_founded: { type: 'number', description: 'Year founded', optional: true }, + company_funding: { type: 'string', description: 'Funding total', optional: true }, + company_revenue: { type: 'string', description: 'Revenue', optional: true }, + company_industry: { type: 'string', description: 'Industry', optional: true }, + company_subindustry: { type: 'string', description: 'Subindustry', optional: true }, + company_linkedin: { type: 'string', description: 'Company LinkedIn URL', optional: true }, + company_location: { type: 'string', description: 'Full company location', optional: true }, + company_description: { type: 'string', description: 'Company description', optional: true }, + credits: { type: 'json', description: 'Remaining credits balance', optional: true }, + }, +} diff --git a/apps/sim/tools/wiza/index.ts b/apps/sim/tools/wiza/index.ts new file mode 100644 index 00000000000..21df6dec38d --- /dev/null +++ b/apps/sim/tools/wiza/index.ts @@ -0,0 +1,6 @@ +export { wizaCompanyEnrichmentTool } from './company_enrichment' +export { wizaGetCreditsTool } from './get_credits' +export { wizaGetIndividualRevealTool } from './get_individual_reveal' +export { wizaProspectSearchTool } from './prospect_search' +export { wizaStartIndividualRevealTool } from './start_individual_reveal' +export type * from './types' diff --git a/apps/sim/tools/wiza/prospect_search.ts b/apps/sim/tools/wiza/prospect_search.ts new file mode 100644 index 00000000000..fd2d420d3cf --- /dev/null +++ b/apps/sim/tools/wiza/prospect_search.ts @@ -0,0 +1,239 @@ +import type { ToolConfig } from '@/tools/types' +import type { WizaProspectSearchParams, WizaProspectSearchResponse } from '@/tools/wiza/types' + +export const wizaProspectSearchTool: ToolConfig< + WizaProspectSearchParams, + WizaProspectSearchResponse +> = { + id: 'wiza_prospect_search', + name: 'Wiza Prospect Search', + description: "Search Wiza's database of prospects using person, company, and financial filters", + version: '1.0.0', + + params: { + apiKey: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Wiza API key', + }, + size: { + type: 'number', + required: false, + visibility: 'user-or-llm', + description: 'Number of sample profiles to return (0-30, default 0)', + }, + filters: { + type: 'object', + required: false, + visibility: 'user-or-llm', + description: 'Full filters object (overrides individual filter params if provided)', + }, + first_name: { + type: 'array', + required: false, + visibility: 'user-or-llm', + description: 'Exact first names to match (e.g., ["John", "Jane"])', + }, + last_name: { + type: 'array', + required: false, + visibility: 'user-or-llm', + description: 'Exact last names to match', + }, + job_title: { + type: 'array', + required: false, + visibility: 'user-or-llm', + description: + 'Job titles to include/exclude (e.g., [{"v":"CEO","s":"i"},{"v":"CTO","s":"e"}])', + }, + job_title_level: { + type: 'array', + required: false, + visibility: 'user-or-llm', + description: 'Seniority levels (e.g., ["cxo", "director", "manager"])', + }, + job_role: { + type: 'array', + required: false, + visibility: 'user-or-llm', + description: 'Job role categories (e.g., ["sales", "engineering", "marketing"])', + }, + job_sub_role: { + type: 'array', + required: false, + visibility: 'user-or-llm', + description: 'Detailed role categories (e.g., ["software", "product"])', + }, + location: { + type: 'array', + required: false, + visibility: 'user-or-llm', + description: "Person's location filters (city/state/country with include/exclude)", + }, + skill: { + type: 'array', + required: false, + visibility: 'user-or-llm', + description: 'Professional skills (e.g., ["python", "marketing"])', + }, + school: { + type: 'array', + required: false, + visibility: 'user-or-llm', + description: 'Educational institutions', + }, + major: { + type: 'array', + required: false, + visibility: 'user-or-llm', + description: 'Field of study', + }, + linkedin_slug: { + type: 'array', + required: false, + visibility: 'user-or-llm', + description: 'LinkedIn profile slugs', + }, + job_company: { + type: 'array', + required: false, + visibility: 'user-or-llm', + description: 'Current company filters (include/exclude)', + }, + past_company: { + type: 'array', + required: false, + visibility: 'user-or-llm', + description: 'Past company filters', + }, + company_location: { + type: 'array', + required: false, + visibility: 'user-or-llm', + description: 'Company HQ location filters', + }, + company_industry: { + type: 'array', + required: false, + visibility: 'user-or-llm', + description: 'Company industry filters (include/exclude)', + }, + company_size: { + type: 'array', + required: false, + visibility: 'user-or-llm', + description: 'Company headcount brackets (e.g., ["1-10", "11-50", "51-200"])', + }, + company_type: { + type: 'array', + required: false, + visibility: 'user-or-llm', + description: 'Company type (e.g., ["private", "public", "educational"])', + }, + }, + + request: { + url: 'https://wiza.co/api/prospects/search', + method: 'POST', + headers: (params: WizaProspectSearchParams) => ({ + Authorization: `Bearer ${params.apiKey}`, + 'Content-Type': 'application/json', + }), + body: (params: WizaProspectSearchParams) => { + const body: Record = {} + + if (typeof params.size === 'number') { + body.size = Math.max(0, Math.min(params.size, 30)) + } + + if (params.filters && typeof params.filters === 'object') { + body.filters = params.filters + return body + } + + const filters: Record = {} + const arrayKeys: Array = [ + 'first_name', + 'last_name', + 'job_title', + 'job_title_level', + 'job_role', + 'job_sub_role', + 'location', + 'skill', + 'school', + 'major', + 'linkedin_slug', + 'job_company', + 'past_company', + 'company_location', + 'company_industry', + 'company_size', + 'company_type', + ] + + for (const key of arrayKeys) { + const value = params[key] + if (Array.isArray(value) && value.length > 0) { + filters[key as string] = value + } + } + + body.filters = filters + return body + }, + }, + + transformResponse: async (response: Response) => { + if (!response.ok) { + const errorText = await response.text() + throw new Error(`Wiza API error: ${response.status} - ${errorText}`) + } + + const data = await response.json() + const payload = data.data ?? {} + const profiles = Array.isArray(payload.profiles) ? payload.profiles : [] + + return { + success: true, + output: { + total: payload.total ?? 0, + profiles: profiles.map((p: Record) => ({ + full_name: (p.full_name as string) ?? null, + linkedin_url: (p.linkedin_url as string) ?? null, + industry: (p.industry as string) ?? null, + job_title: (p.job_title as string) ?? null, + job_title_role: (p.job_title_role as string) ?? null, + job_title_sub_role: (p.job_title_sub_role as string) ?? null, + job_company_name: (p.job_company_name as string) ?? null, + job_company_website: (p.job_company_website as string) ?? null, + location_name: (p.location_name as string) ?? null, + })), + }, + } + }, + + outputs: { + total: { type: 'number', description: 'Total number of matching prospects' }, + profiles: { + type: 'array', + description: 'Sample profiles matching the filter criteria', + items: { + type: 'object', + properties: { + full_name: { type: 'string' }, + linkedin_url: { type: 'string' }, + industry: { type: 'string' }, + job_title: { type: 'string' }, + job_title_role: { type: 'string' }, + job_title_sub_role: { type: 'string' }, + job_company_name: { type: 'string' }, + job_company_website: { type: 'string' }, + location_name: { type: 'string' }, + }, + }, + }, + }, +} diff --git a/apps/sim/tools/wiza/start_individual_reveal.ts b/apps/sim/tools/wiza/start_individual_reveal.ts new file mode 100644 index 00000000000..10955e4c548 --- /dev/null +++ b/apps/sim/tools/wiza/start_individual_reveal.ts @@ -0,0 +1,142 @@ +import type { ToolConfig } from '@/tools/types' +import type { + WizaStartIndividualRevealParams, + WizaStartIndividualRevealResponse, +} from '@/tools/wiza/types' + +export const wizaStartIndividualRevealTool: ToolConfig< + WizaStartIndividualRevealParams, + WizaStartIndividualRevealResponse +> = { + id: 'wiza_start_individual_reveal', + name: 'Wiza Start Individual Reveal', + description: + 'Start an individual reveal to enrich a contact via LinkedIn URL, name+company, or email', + version: '1.0.0', + + params: { + apiKey: { + type: 'string', + required: true, + visibility: 'user-only', + description: 'Wiza API key', + }, + enrichment_level: { + type: 'string', + required: true, + visibility: 'user-or-llm', + description: 'Enrichment depth: none, partial, phone, or full', + }, + profile_url: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'LinkedIn profile URL (e.g., https://linkedin.com/in/johndoe)', + }, + full_name: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Full name (used with company or domain)', + }, + company: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Company name (used with full_name)', + }, + domain: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Company domain (used with full_name)', + }, + email: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Email address (use alone or with other identifiers)', + }, + accept_work: { + type: 'boolean', + required: false, + visibility: 'user-or-llm', + description: 'Whether to accept work emails (email_options)', + }, + accept_personal: { + type: 'boolean', + required: false, + visibility: 'user-or-llm', + description: 'Whether to accept personal emails (email_options)', + }, + callback_url: { + type: 'string', + required: false, + visibility: 'user-or-llm', + description: 'Optional URL to receive a callback with the reveal update', + }, + }, + + request: { + url: 'https://wiza.co/api/individual_reveals', + method: 'POST', + headers: (params: WizaStartIndividualRevealParams) => ({ + Authorization: `Bearer ${params.apiKey}`, + 'Content-Type': 'application/json', + }), + body: (params: WizaStartIndividualRevealParams) => { + const individual: Record = {} + if (params.profile_url) individual.profile_url = params.profile_url + if (params.full_name) individual.full_name = params.full_name + if (params.company) individual.company = params.company + if (params.domain) individual.domain = params.domain + if (params.email) individual.email = params.email + + const body: Record = { + individual_reveal: individual, + enrichment_level: params.enrichment_level, + } + + if (params.accept_work !== undefined || params.accept_personal !== undefined) { + const emailOptions: Record = {} + if (params.accept_work !== undefined) emailOptions.accept_work = params.accept_work + if (params.accept_personal !== undefined) { + emailOptions.accept_personal = params.accept_personal + } + body.email_options = emailOptions + } + + if (params.callback_url) body.callback_url = params.callback_url + + return body + }, + }, + + transformResponse: async (response: Response) => { + if (!response.ok) { + const errorText = await response.text() + throw new Error(`Wiza API error: ${response.status} - ${errorText}`) + } + + const json = await response.json() + const d = json.data ?? {} + + return { + success: true, + output: { + id: d.id ?? null, + status: d.status ?? null, + is_complete: d.is_complete ?? null, + }, + } + }, + + outputs: { + id: { type: 'number', description: 'Individual reveal ID (use with Get Individual Reveal)' }, + status: { + type: 'string', + description: 'Reveal status: queued, resolving, finished, or failed', + }, + is_complete: { type: 'boolean', description: 'Whether the reveal has completed' }, + }, +} diff --git a/apps/sim/tools/wiza/types.ts b/apps/sim/tools/wiza/types.ts new file mode 100644 index 00000000000..f75e794f622 --- /dev/null +++ b/apps/sim/tools/wiza/types.ts @@ -0,0 +1,178 @@ +import type { ToolResponse } from '@/tools/types' + +export interface WizaGetCreditsParams { + apiKey: string +} + +export interface WizaGetCreditsResponse extends ToolResponse { + output: { + email_credits: number | string | null + phone_credits: number | string | null + export_credits: number | null + api_credits: number | null + } +} + +export interface WizaProspectSearchParams { + apiKey: string + size?: number + filters?: Record + first_name?: string[] + last_name?: string[] + job_title?: unknown[] + job_title_level?: string[] + job_role?: string[] + job_sub_role?: string[] + location?: unknown[] + skill?: string[] + school?: string[] + major?: string[] + linkedin_slug?: string[] + job_company?: unknown[] + past_company?: unknown[] + company_location?: unknown[] + company_industry?: unknown[] + company_size?: string[] + company_type?: string[] +} + +interface WizaProspectProfile { + full_name: string | null + linkedin_url: string | null + industry: string | null + job_title: string | null + job_title_role: string | null + job_title_sub_role: string | null + job_company_name: string | null + job_company_website: string | null + location_name: string | null +} + +export interface WizaProspectSearchResponse extends ToolResponse { + output: { + total: number + profiles: WizaProspectProfile[] + } +} + +export interface WizaCompanyEnrichmentParams { + apiKey: string + company_name?: string + company_domain?: string + company_linkedin_id?: string + company_linkedin_slug?: string +} + +export interface WizaCompanyEnrichmentResponse extends ToolResponse { + output: { + company_name: string | null + company_domain: string | null + domain: string | null + company_industry: string | null + company_size: number | null + company_size_range: string | null + company_founded: number | null + company_revenue_range: string | null + company_funding: string | null + company_type: string | null + company_description: string | null + company_ticker: string | null + company_last_funding_round: string | null + company_last_funding_amount: string | null + company_last_funding_at: string | null + company_location: string | null + company_twitter: string | null + company_facebook: string | null + company_linkedin: string | null + company_linkedin_id: string | null + company_street: string | null + company_locality: string | null + company_region: string | null + company_postal_code: string | null + company_country: string | null + credits: Record | null + } +} + +export interface WizaStartIndividualRevealParams { + apiKey: string + enrichment_level: 'none' | 'partial' | 'phone' | 'full' + profile_url?: string + full_name?: string + company?: string + domain?: string + email?: string + accept_work?: boolean + accept_personal?: boolean + callback_url?: string +} + +interface WizaIndividualRevealData { + id: number | null + status: string | null + is_complete: boolean | null + name: string | null + company: string | null + enrichment_level: string | null + linkedin_profile_url: string | null + title: string | null + location: string | null + email: string | null + email_type: string | null + email_status: string | null + emails: Array<{ + email: string | null + email_type: string | null + email_status: string | null + }> + mobile_phone: string | null + phone_number: string | null + phone_status: string | null + phones: Array<{ + number: string | null + pretty_number: string | null + type: string | null + }> + company_size: number | null + company_size_range: string | null + company_type: string | null + company_domain: string | null + company_locality: string | null + company_region: string | null + company_country: string | null + company_street: string | null + company_postal_code: string | null + company_founded: number | null + company_funding: string | null + company_revenue: string | null + company_industry: string | null + company_subindustry: string | null + company_linkedin: string | null + company_location: string | null + company_description: string | null + credits: Record | null +} + +export interface WizaStartIndividualRevealResponse extends ToolResponse { + output: { + id: number | null + status: string | null + is_complete: boolean | null + } +} + +export interface WizaGetIndividualRevealParams { + apiKey: string + id: string +} + +export interface WizaGetIndividualRevealResponse extends ToolResponse { + output: WizaIndividualRevealData +} + +export type WizaResponse = + | WizaGetCreditsResponse + | WizaProspectSearchResponse + | WizaCompanyEnrichmentResponse + | WizaStartIndividualRevealResponse + | WizaGetIndividualRevealResponse From dc7bab0a91e9f91118091d78c65a60ccded37e8d Mon Sep 17 00:00:00 2001 From: Waleed Latif Date: Tue, 19 May 2026 11:20:51 -0700 Subject: [PATCH 2/3] fix(wiza): coerce reveal id to string, skip empty filters in prospect search --- apps/sim/tools/wiza/get_individual_reveal.ts | 2 +- apps/sim/tools/wiza/prospect_search.ts | 11 +++++++++-- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/apps/sim/tools/wiza/get_individual_reveal.ts b/apps/sim/tools/wiza/get_individual_reveal.ts index bbf13e8dcba..52661bb8482 100644 --- a/apps/sim/tools/wiza/get_individual_reveal.ts +++ b/apps/sim/tools/wiza/get_individual_reveal.ts @@ -30,7 +30,7 @@ export const wizaGetIndividualRevealTool: ToolConfig< request: { url: (params: WizaGetIndividualRevealParams) => - `https://wiza.co/api/individual_reveals/${encodeURIComponent(params.id.trim())}`, + `https://wiza.co/api/individual_reveals/${encodeURIComponent(String(params.id).trim())}`, method: 'GET', headers: (params: WizaGetIndividualRevealParams) => ({ Authorization: `Bearer ${params.apiKey}`, diff --git a/apps/sim/tools/wiza/prospect_search.ts b/apps/sim/tools/wiza/prospect_search.ts index fd2d420d3cf..c507f0cbb61 100644 --- a/apps/sim/tools/wiza/prospect_search.ts +++ b/apps/sim/tools/wiza/prospect_search.ts @@ -148,7 +148,12 @@ export const wizaProspectSearchTool: ToolConfig< body.size = Math.max(0, Math.min(params.size, 30)) } - if (params.filters && typeof params.filters === 'object') { + if ( + params.filters && + typeof params.filters === 'object' && + !Array.isArray(params.filters) && + Object.keys(params.filters).length > 0 + ) { body.filters = params.filters return body } @@ -181,7 +186,9 @@ export const wizaProspectSearchTool: ToolConfig< } } - body.filters = filters + if (Object.keys(filters).length > 0) { + body.filters = filters + } return body }, }, From 8ad7d0eab0693fd7c151ee3250b47edb56737e4f Mon Sep 17 00:00:00 2001 From: Waleed Latif Date: Tue, 19 May 2026 11:35:09 -0700 Subject: [PATCH 3/3] fix(wiza): throw on invalid JSON in advanced filter fields instead of silently dropping --- apps/sim/blocks/blocks/wiza.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/apps/sim/blocks/blocks/wiza.ts b/apps/sim/blocks/blocks/wiza.ts index 04fc870cc85..938e9318449 100644 --- a/apps/sim/blocks/blocks/wiza.ts +++ b/apps/sim/blocks/blocks/wiza.ts @@ -341,8 +341,10 @@ export const WizaBlock: BlockConfig = { if (typeof value === 'string' && value.trim() !== '') { try { parsed[field] = JSON.parse(value) - } catch { - // Leave as-is if not valid JSON; tool may still accept the string + } catch (err) { + throw new Error( + `Invalid JSON in Wiza "${field}" filter: ${err instanceof Error ? err.message : String(err)}` + ) } } }