mirror of
https://github.com/simstudioai/sim.git
synced 2026-10-16 14:50:06 +08:00
feat(integrations): add ZoomInfo, align Wiza, audit Apollo, refresh docs (#4776)
* feat(integrations): add ZoomInfo, align Wiza, audit Apollo, refresh docs - Add ZoomInfo integration: search/enrich contacts & companies, intent, news (6 tools), proxy route, block, and icon - Validate and align Wiza tools/block/outputs against live API docs - Audit Apollo tools: tighten params, outputs, and types - Update tool docs (.mdx), icons, icon mappings, and integrations.json * fix(zoominfo): use useId for ZoomInfoIcon clipPath to avoid duplicate DOM ids Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix(apollo): address PR review on sequence add and bulk enrich - sequence_add_contacts: send large contact_ids/label_names arrays in the POST body (Rails merges query + body params) to avoid reverse-proxy URL length limits; keep scalar settings in the query string - organization_bulk_enrich: add back-compat shim mapping the legacy `organizations` ({name, domain}[]) subBlock value to the new `domains` string array so saved workflows keep running Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix(integrations): unique ZoomInfo icon clip id, numeric employee range filters - ZoomInfoIcon: derive clipPath id from useId() so multiple instances don't collide - ZoomInfo company search: send employeeRangeMin/Max as numbers, matching revenueMin/Max * fix(zoominfo): send employeeRange filters as strings per API schema Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix(zoominfo): send contactAccuracyScoreMin as string per Contacts Search schema Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix(apollo): harden people_search pagination, correct bulk-update output docs - people_search: read pagination from both the nested `pagination` object (legacy /mixed_people/search) and top-level fields, avoiding silent fallback to defaults - account_bulk_update: correct output descriptions — accounts support up to 1000 per request and async is opt-in (not auto-triggered at 100 like contacts) Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix(wiza): correct company enrichment credits shape in output docs Company enrichment returns api_credits { total, company_credits }, not the email/phone/scrape breakdown used by individual reveals. Description-only fix verified against docs.wiza.co. * fix(apollo): send sequence add contact_ids/label_names as query params per docs Apollo documents every field for emailer_campaigns/:id/add_contact_ids as a query parameter with no request body. Append contact_ids[]/label_names[] to the query string instead of the JSON body to match the documented contract. * feat(apollo): expose account_stage_id uniform field for bulk update accounts Apollo documents account_stage_id as a Body Param for /accounts/bulk_update ('when using account_ids, apply this account stage to all accounts'). Adds it to the tool params, body builder, type, block subBlock, and params mapper alongside name/owner_id. * docs(apollo): correct contact_update typed_custom_fields description Apollo's update-a-contact endpoint documents typed_custom_fields, so drop the inaccurate "not officially documented" caveat. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix(zoominfo): default required outputFields on enrich; parse nested API error object - ZoomInfo enrich endpoints require outputFields; send a curated default set when omitted so requests don't fail - extractZoomInfoError now reads the GTM REST nested error object ({error:{code,message}}) instead of dropping it to a generic HTTP message Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * improvement(wiza): add wandConfig to complex prospect-search filter fields Adds AI-assist wandConfig (json-object) with format examples to the structured filter inputs (job_title, job_company, past_company, company_industry, location, company_location) and the full filters object, completing the wandConfig checklist item for the Wiza block. * fix(findymail): surface API .error messages and alphabetize registry - transformResponse error branches now fall back to the response body's `error` field before the generic status string, so Findymail's actual messages ("Not enough credits" on 402, "Subscription is paused" on 423, "One identifier is required..." on 422) reach the user instead of a bare "Findymail API error: <status>". Applied to all 11 tools. - alphabetize the findymail entries in tools/registry.ts to match the already-alphabetical import block and the integration guideline. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.8
parent
53eaa600af
commit
c898e2e623
@@ -30,29 +30,44 @@ export function AgentMailIcon(props: SVGProps<SVGSVGElement>) {
|
||||
|
||||
export function AgentPhoneIcon(props: SVGProps<SVGSVGElement>) {
|
||||
return (
|
||||
<svg {...props} viewBox='0 0 150 150' xmlns='http://www.w3.org/2000/svg'>
|
||||
<svg {...props} viewBox='0 0 236 240' fill='none' xmlns='http://www.w3.org/2000/svg'>
|
||||
<path
|
||||
fill='#23AF58'
|
||||
stroke='#007F3F'
|
||||
strokeWidth='0.15'
|
||||
strokeMiterlimit='10'
|
||||
d='m139.6 53.3c-1.4-2.3-4.9-3.3-7.6-4.8-2.7-1.3-4.2-2.4-5.7-3.6-1.9-1-2.5-2.7-3.3-3.2s-2.7-1.4-4.5 1.3c-2 2.7-4.5 6.6-6.6 11.1-2.3 5.4-6.3 14.9-6.3 18.9 0.5 4.9 3.1 4.6 6.1 7.2 2.5 2.1 2.8 5.8 1.5 12.5-1.3 6.6-4 12.8-7.8 19.2-3.3 5.1-5.8 8.7-10 9.1-5.3 0.5-12.5-3.1-16.8-5.6-1-0.6-2.5-0.9-3.8-0.2-1.3 0.5-2.2 1.6-3.2 3.3-1.5 2.5-4.6 7.7-5.8 12.2-0.5 3 0 6.4 2.9 9 1.4 1.2 2.8 2.5 4.4 3.4 5 2.8 9.6 4.5 16.5 4.9 5.3 0.2 9.3-1 13.4-3.1 2.4-1.3 6.6-4.2 9.6-7.3l1.1-1.2c2.8-3.1 8.8-10 11.6-14.5 2.3-3.5 4.8-7.4 6.9-12.3 2.9-6.7 4.4-14 5-17.9 1.2-7 2.4-17.5 3.4-31.1 0.1-4.3-0.3-6.1-1-7.3zm-4.5 6.7c-0.5 9.5-1.9 23.3-3.1 30.1-0.9 4.5-2.4 9.6-3.8 13.4-1.1 2.6-3.1 7-5.6 10.8-3.4 5.3-8.4 11.6-12 15.8-6.4 6.6-10.2 9.6-14.2 10.8-2.2 0.9-3.8 1.2-7 1.2-3.4-0.1-8-0.7-11.3-2.2-3-1.2-7-4-6.9-6.8 0.4-3.2 3.3-9.6 5.2-11.9 0.2-0.3 0.5-0.3 0.7-0.2 2.5 1.1 6 3.2 9.6 4.5 2.4 0.9 4.8 1.4 7.3 1.4 3.9 0 6.7-1.2 9.5-3.2 5.6-4.6 9-10.8 12.1-17.5 2-4.3 4.1-11.6 4.4-18.3 0.1-4.9-1.1-8.9-4.5-12.2-1.1-0.7-3-2.1-3-2.8 0-4.2 3.9-13 8.9-22.9 0.2-0.7 0.5-1 1.1-0.7 1.1 0.6 3 1.4 4.6 2.4 2.1 1 5.4 2.4 7.1 3.9 0.9 0.4 1 3 0.9 4.4z'
|
||||
fillRule='evenodd'
|
||||
clipRule='evenodd'
|
||||
d='M113.144 79.3516C126.849 79.3516 138.665 80.2967 143.628 85.2591C149.299 90.9306 151.662 103.455 151.662 120.705C151.662 129.921 150.481 136.538 146.936 141.737C140.32 151.19 119.761 154.734 95.6569 155.443C76.8148 155.443 70.5663 153.611 66.7692 149.046L66.5903 148.827C61.8642 143.628 59.501 129.922 58.7921 114.089C58.7921 108.181 59.0286 100.147 61.3917 95.1842C64.5966 88.3165 68.9105 86.1058 77.5557 83.611L78.4062 83.3683C89.5331 80.0303 104.452 79.3516 113.144 79.3516ZM103.868 23.8233C105.684 23.9222 107.236 25.5316 107.236 27.363L107.231 27.516C107.145 29.3418 105.531 30.9074 103.691 30.9075L103.536 30.9035C101.921 30.8184 100.147 29.4233 100.147 27.363C100.147 27.2958 100.149 27.2288 100.153 27.1623C100.271 25.257 101.99 23.7721 103.868 23.8233ZM92.3822 114.379C91.7601 109.217 89.9576 101.341 83.841 101.568C78.406 102.041 75.3339 107.948 76.0428 116.455C76.5154 122.599 79.115 129.216 84.3137 129.216C91.6341 129.704 92.9389 121.325 92.5283 115.743C92.5003 115.363 92.4465 114.913 92.3822 114.379ZM119.287 112.166C118.753 106.772 120.089 97.9051 126.849 97.3172C133.466 97.5536 135.829 106.769 136.065 112.204C136.302 116.222 135.593 120.948 133.23 123.311C131.575 125.201 127.322 125.674 124.959 124.02C121.501 122.176 119.842 116.731 119.325 112.517L119.287 112.166ZM106.078 135.137C104.869 135.173 103.537 135.175 102.037 135.128C101.584 135.128 101.069 135.084 100.534 135.039C98.2803 134.848 95.657 134.627 95.6569 137.491C95.6569 141.508 100.855 144.58 107.235 145.053C115.506 145.289 120.941 139.382 120.705 136.31C120.705 134.656 119.76 133.001 117.633 133.474C117.046 133.591 116.481 133.709 115.927 133.824C112.836 134.466 109.926 135.019 106.078 135.137Z'
|
||||
fill='white'
|
||||
/>
|
||||
<path
|
||||
fill='#23AF58'
|
||||
d='m104.7 27.8c-1.3-1.5-3.3-1.3-6.2-1.5l-1.9 0.2-7-0.2-31.5 0.2 1.5-9.3c2-1.1 5.1-3.5 5.8-6.3 1-2.8 0.2-5.9-2-7.4-2.3-1.9-5.8-2.4-9.3-0.8-1.6 1-4.7 3.4-5.4 6.9-0.8 4.1 2.4 6.7 4.7 7.9l-1.5 9.1-17.2 0.9c-12.3 1.1-16.3 1.2-20.6 4.3-2 1.3-3 4.5-3.4 9.8-0.6 11.3-0.7 18.7-0.6 28.3 0.4 11.2 0 36.6 3 39.8l-1.2 0.3c-3.8 0.6-4 6.2-0.5 6.6l15.5-1 69.7-7.6c2.5-0.4 4.3-0.9 4.6-4.3l3.7-71.5c0-1.9 0.2-3.6-0.2-4.4zm-49.6-17.3c0.3-2.2 2.4-3 3.3-2.8 0.7 0.4 1 1.8 0 2.8-1.5 2-3.3 1.7-3.3 0zm40 90.2c-4 1-5.5 1.5-11.5 2.4-7.7 1-19.7 2.1-31.2 3.4l-33.8 2.9c-0.7 0.2-1-0.4-1-1-0.6-6.5-1.2-20.5-1.5-39.5l0.3-23.3c0.6-7.5 0.7-8.7 4.6-9.7 5.1-0.9 7.4-1.4 14.9-1.8l19.5-0.5 41.1-0.5c1.4 0 1.9 0.4 1.9 1.5l-3.3 66.1z'
|
||||
fillRule='evenodd'
|
||||
clipRule='evenodd'
|
||||
d='M17.9127 164.383C18.008 173.453 21.1026 182.054 26.8907 189C37.0521 200.815 55.9566 210.504 79.5873 211.922C83.1319 212.158 87.1496 211.685 90.2217 210.976C98.4924 208.85 103.455 202.705 103.455 195.143C103.455 188.999 98.9651 179.311 85.9683 177.657C70.773 176.441 55.4867 174.057 45.3225 161.351C51.7029 165.369 62.5734 169.858 81.2419 170.567C90.4497 170.567 95.3749 172.384 103.441 175.613L103.641 175.389C111.111 167.007 116.993 160.406 128.031 160.406C134.033 160.256 138.129 162.487 141.046 164.076C142.723 164.99 144.011 165.691 145.046 165.605C148.355 165.367 164.66 145.045 168.677 132.994C169.297 130.926 169.375 129.22 169.384 128.509C169.393 126.948 167.209 125.547 165.778 124.629L165.762 124.619C165.526 124.468 165.311 124.33 165.131 124.207C164.405 123.712 163.73 123.251 163.242 122.832C158.752 119.524 154.971 114.089 154.971 108.417C154.971 99.4375 160.879 86.9131 167.968 75.334C164.991 67.5485 159.13 58.2099 147.374 53.7683L146.809 53.5606C133.948 49.0707 118.229 48.4536 104.637 49.8128L106.527 36.8154C109.599 35.8702 113.616 33.0344 113.616 27.8357C113.849 23.1833 109.96 18.073 103.978 17.9145L103.691 17.9105C98.0199 17.9105 94.0026 22.4008 93.53 26.8907C93.2939 31.3804 96.1297 34.9247 99.438 36.5788L97.3111 49.8128C87.8587 50.5217 78.8788 51.4668 68.9538 53.3573C61.8583 54.8852 54.5424 56.4829 47.9859 59.7016C39.4376 63.9402 32.7425 69.8213 29.2533 79.8238C25.7086 88.0947 24.7636 105.582 24.5273 123.069L25.2361 141.265C20.979 148.205 17.9106 155.63 17.9105 163.95L17.9127 164.383ZM143.628 85.2591C138.665 80.2967 126.849 79.3516 113.144 79.3516C104.452 79.3516 89.5331 80.0303 78.4062 83.3683L77.5557 83.611C68.9105 86.1058 64.5966 88.3165 61.3917 95.1842C59.0286 100.147 58.7921 108.181 58.7921 114.089C59.501 129.922 61.8642 143.628 66.5903 148.827L66.7692 149.046C70.5663 153.611 76.8148 155.443 95.6569 155.443C119.761 154.734 140.32 151.19 146.936 141.737C150.481 136.538 151.662 129.921 151.662 120.705C151.662 103.455 149.299 90.9306 143.628 85.2591ZM107.236 27.363C107.236 25.5316 105.684 23.9222 103.868 23.8233C101.99 23.7721 100.271 25.257 100.153 27.1623C100.149 27.2288 100.147 27.2958 100.147 27.363C100.147 29.4233 101.921 30.8184 103.536 30.9035L103.691 30.9075C105.531 30.9074 107.145 29.3418 107.231 27.516L107.236 27.363Z'
|
||||
fill='#171717'
|
||||
/>
|
||||
<path
|
||||
fill='#23AF58'
|
||||
d='m38.9 52.4c-1.8 0-4 1.1-4.5 3.3-1 3.9 1 7.6 4.5 7.7 3.8 0 5-3.8 4.7-6.3-0.2-2-2-4.7-4.7-4.7z'
|
||||
d='M92.3822 114.379C91.7601 109.217 89.9576 101.341 83.841 101.568C78.406 102.041 75.3339 107.948 76.0428 116.455C76.5154 122.599 79.115 129.216 84.3137 129.216C91.6341 129.704 92.9389 121.325 92.5283 115.743C92.5003 115.363 92.4465 114.913 92.3822 114.379Z'
|
||||
fill='#171717'
|
||||
/>
|
||||
<path
|
||||
fill='#23AF58'
|
||||
d='m73.5 53.9c-1.8 0-4.3 1.5-4.4 4.5-0.1 3.2 2 5.3 4.3 5.3 2.5 0 4.2-1.7 4.2-4.8 0-3.2-1.7-4.8-4.1-5z'
|
||||
d='M106.078 135.137C104.869 135.173 103.537 135.175 102.037 135.128C101.584 135.128 101.069 135.084 100.534 135.039C98.2803 134.848 95.657 134.627 95.6569 137.491C95.6569 141.508 100.855 144.58 107.235 145.053C115.506 145.289 120.941 139.382 120.705 136.31C120.705 134.656 119.76 133.001 117.633 133.474C117.046 133.591 116.481 133.709 115.927 133.824C112.836 134.466 109.926 135.019 106.078 135.137Z'
|
||||
fill='#171717'
|
||||
/>
|
||||
<path
|
||||
fill='#23AF58'
|
||||
d='m72.1 77.1c-2.7 3.4-7.2 7.4-14.7 8.3-7.3 0.3-13.9-2.9-20-8.5-3.5-3.4-8 0-6.2 2.7 1.7 2.5 6.4 6.6 10.4 8.8 3.5 2 7.3 3.3 13.8 3.5 4.7 0 9.2-0.8 12.7-2.4 2.9-1.1 5-2.8 6-3.8 2.3-2.1 3.8-4.1 3.5-7.3-0.9-2.5-3.6-2.8-5.5-1.3z'
|
||||
d='M126.849 97.3172C120.089 97.9051 118.753 106.772 119.287 112.166L119.325 112.517C119.842 116.731 121.501 122.176 124.959 124.02C127.322 125.674 131.575 125.201 133.23 123.311C135.593 120.948 136.302 116.222 136.065 112.204C135.829 106.769 133.466 97.5536 126.849 97.3172Z'
|
||||
fill='#171717'
|
||||
/>
|
||||
<path
|
||||
d='M185.219 73.4437C174.348 74.1526 165.132 91.6394 162.06 104.873C161.115 109.835 163.242 114.089 166.314 117.397C167.351 118.261 168.443 119.027 169.503 119.77C172.792 122.074 175.766 124.157 175.766 128.268C175.643 141.817 163.304 156.584 154.498 165.841C149.063 171.749 145.754 174.584 138.901 170.567C131.812 165.841 124.959 164.187 117.161 171.276C113.853 174.348 109.599 179.311 106.764 183.092C108.181 186.164 109.363 189.472 109.363 194.907C109.126 199.397 107.236 204.596 104.4 209.086C124.532 231.455 155.463 220.906 173.89 202.636C178.181 198.381 182.187 193.802 185.219 189.709C185.567 189.238 185.576 189.054 185.219 188.763C181.438 185.691 178.129 179.783 178.129 171.513C178.129 160.17 186.394 147.984 197.034 146.464C200.502 145.968 203.651 145.754 209.558 147.409C214.285 135.593 217.829 122.833 217.593 111.253C217.593 89.7493 201.76 73.9165 185.219 73.4437Z'
|
||||
fill='#171717'
|
||||
/>
|
||||
<path
|
||||
d='M201.76 151.191C194.671 150.955 188.763 154.973 185.691 162.062C182.855 169.151 183.564 177.185 189.945 183.093C192.308 185.456 195.38 188.292 201.76 187.82C206.339 186.904 210.698 184.435 213.544 179.982C217.787 173.223 219.336 163.499 213.576 157.099C212.867 156.154 211.212 154.5 209.322 153.554C206.722 151.9 204.123 151.191 201.76 151.191Z'
|
||||
fill='#171717'
|
||||
/>
|
||||
<path
|
||||
fillRule='evenodd'
|
||||
clipRule='evenodd'
|
||||
d='M151.95 36.3891C145.291 34.1404 138.251 32.7457 131.234 31.9893C133.604 15.5474 120.526 0 103.691 0C88.0053 2.13949e-05 77.0585 12.2798 75.7179 25.0157C75.404 28.0007 75.6665 31.074 76.3755 33.9837C72.1393 34.5787 67.9129 35.2599 63.7306 36.1608L63.7017 36.1669L63.6733 36.1731C41.5302 41.016 21.0899 49.6694 12.5431 73.3623C6.35339 88.5152 6.83579 106.747 6.6185 122.827L7.1406 136.86C3.17535 144.406 4.86162e-05 153.458 0 163.95C6.1323e-06 177.201 4.61489 190.596 13.3109 200.678C31.0027 221.249 69.4846 237.244 97.8231 227.374C107.065 234.696 119.549 239.953 133.779 239.992C152.316 240.281 166.927 232.388 177.969 223.12C184.191 217.971 190.489 211.587 195.611 205.442C209.558 207 220.449 197.5 226 187.82C232.491 176.5 235 165 228.722 147.33C232.661 136.158 235.728 123.512 235.502 111.051C235.402 81.2695 213.171 56.3248 185.73 55.5406C183.203 55.5867 180.728 55.8668 178.279 56.5314C171.906 47.1088 162.702 40.0778 151.95 36.3891ZM185.219 73.4437C174.348 74.1526 165.132 91.6394 162.06 104.873C161.115 109.835 163.242 114.089 166.314 117.397C167.351 118.261 168.443 119.027 169.503 119.77C172.792 122.074 175.766 124.157 175.766 128.268C175.643 141.817 163.304 156.584 154.498 165.841C149.063 171.749 145.754 174.584 138.901 170.567C131.812 165.841 124.959 164.187 117.161 171.276C113.853 174.348 109.599 179.311 106.764 183.092C108.181 186.164 109.363 189.472 109.363 194.907C109.126 199.397 107.236 204.596 104.4 209.086C124.532 231.455 155.463 220.906 173.89 202.636C178.181 198.381 182.187 193.802 185.219 189.709C185.567 189.238 185.576 189.054 185.219 188.763C181.438 185.691 178.129 179.783 178.129 171.513C178.129 160.17 186.394 147.984 197.034 146.464C200.502 145.968 203.651 145.754 209.558 147.409C214.285 135.593 217.829 122.833 217.593 111.253C217.593 89.7493 201.76 73.9165 185.219 73.4437ZM201.76 151.191C194.671 150.955 188.763 154.973 185.691 162.062C182.855 169.151 183.564 177.185 189.945 183.093C192.308 185.456 195.38 188.292 201.76 187.82C206.339 186.904 210.698 184.435 213.544 179.982C217.787 173.223 219.336 163.499 213.576 157.099C212.867 156.154 211.212 154.5 209.322 153.554C206.722 151.9 204.123 151.191 201.76 151.191ZM17.9127 164.383C18.008 173.453 21.1026 182.054 26.8907 189C37.0521 200.815 55.9566 210.504 79.5873 211.922C83.1319 212.158 87.1496 211.685 90.2217 210.976C98.4924 208.85 103.455 202.705 103.455 195.143C103.455 188.999 98.9651 179.311 85.9683 177.657C70.773 176.441 55.4867 174.057 45.3225 161.351C51.7029 165.369 62.5734 169.858 81.2419 170.567C90.4497 170.567 95.3749 172.384 103.441 175.613L103.641 175.389C111.111 167.007 116.993 160.406 128.031 160.406C134.033 160.256 138.129 162.487 141.046 164.076C142.723 164.99 144.011 165.691 145.046 165.605C148.355 165.367 164.66 145.045 168.677 132.994C169.297 130.926 169.375 129.22 169.384 128.509C169.393 126.948 167.209 125.547 165.778 124.629L165.762 124.619C165.526 124.468 165.311 124.33 165.131 124.207C164.405 123.712 163.73 123.251 163.242 122.832C158.752 119.524 154.971 114.089 154.971 108.417C154.971 99.4375 160.879 86.9131 167.968 75.334C164.991 67.5485 159.13 58.2099 147.374 53.7683L146.809 53.5606C133.948 49.0707 118.229 48.4536 104.637 49.8128L106.527 36.8154C109.599 35.8702 113.616 33.0344 113.616 27.8357C113.849 23.1833 109.96 18.073 103.978 17.9145L103.691 17.9105C98.0199 17.9105 94.0026 22.4008 93.53 26.8907C93.2939 31.3804 96.1297 34.9247 99.438 36.5788L97.3111 49.8128C87.8587 50.5217 78.8788 51.4668 68.9538 53.3573C61.8583 54.8852 54.5424 56.4829 47.9859 59.7016C39.4376 63.9402 32.7425 69.8213 29.2533 79.8238C25.7086 88.0947 24.7636 105.582 24.5273 123.069L25.2361 141.265C20.979 148.205 17.9106 155.63 17.9105 163.95L17.9127 164.383Z'
|
||||
fill='white'
|
||||
/>
|
||||
</svg>
|
||||
)
|
||||
@@ -536,7 +551,7 @@ export function HubspotIcon(props: SVGProps<SVGSVGElement>) {
|
||||
xmlns='http://www.w3.org/2000/svg'
|
||||
fill='currentColor'
|
||||
>
|
||||
<path d='M18.16 7.93V5.08a2.2 2.2 0 1.27-1.98v-.067A2.2 2.2 0 17.24.845h-.067a2.2 2.2 0 00-2.19 2.19v.067a2.2 2.2 0 1.25 1.97l.13.01v2.85a6.22 6.22 0 00-2.97 1.31l.012-.01-7.83-6.09A2.5 2.5 0 104.3 4.66l-.12.01 7.7 5.99a6.18 6.18 0 00-1.04 3.45c0 1.34.425 2.59 1.15 3.61l-.013-.02-2.34 2.34a1.97 1.97 0 00-.58-.095h-.002a2.03 2.03 0 102.03 2.03 1.98 1.98 0 00-.1-.595l.5.01 2.32-2.32a6.25 6.25 0 104.78-11.13l-.036-.005zm-.964 9.38a3.21 3.21 0 113.22-3.21v.002a3.21 3.21 0 01-3.21 3.21z' />
|
||||
<path d='M18.164 7.93V5.084a2.198 2.198 0 0 0 1.27-1.978v-.067A2.2 2.2 0 0 0 17.238.845h-.067a2.2 2.2 0 0 0-2.193 2.194v.067a2.196 2.196 0 0 0 1.27 1.978v2.85a6.21 6.21 0 0 0-2.974 1.31L5.443.515A2.5 2.5 0 1 0 4.3 4.664l-.123.013 7.728 6.013a6.182 6.182 0 0 0-1.043 3.45c0 1.336.43 2.605 1.157 3.62l-2.35 2.348a2.022 2.022 0 0 0-.585-.1 2.026 2.026 0 1 0 2.026 2.026 1.98 1.98 0 0 0-.1-.584l2.318-2.323A6.249 6.249 0 1 0 18.166 7.93Zm-.96 9.371a3.21 3.21 0 1 1 0-6.421 3.21 3.21 0 0 1 0 6.42z' />
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
@@ -2296,23 +2311,26 @@ export function ElevenLabsIcon(props: SVGProps<SVGSVGElement>) {
|
||||
}
|
||||
|
||||
export function FindymailIcon(props: SVGProps<SVGSVGElement>) {
|
||||
const id = useId()
|
||||
const gradient0 = `findymail_paint0_${id}`
|
||||
const gradient1 = `findymail_paint1_${id}`
|
||||
return (
|
||||
<svg {...props} xmlns='http://www.w3.org/2000/svg' viewBox='0 0 45.4462 31.2952' fill='none'>
|
||||
<path
|
||||
fillRule='evenodd'
|
||||
clipRule='evenodd'
|
||||
d='M27.7788 8.18066C26.628 8.18066 25.479 8.62026 24.6016 9.49766L22.7235 11.0666L20.8454 9.49766C19.968 8.62026 18.8189 8.18066 17.6681 8.18066C16.5191 8.18066 15.3683 8.62026 14.4909 9.49766C12.7361 11.2525 12.7361 14.0991 14.4909 15.8521L20.9722 22.0922C21.9497 23.0339 23.4972 23.0339 24.4747 22.0922L30.9578 15.8521C32.7126 14.0991 32.7126 11.2525 30.9578 9.49766C30.0804 8.62026 28.9296 8.18066 27.7788 8.18066Z'
|
||||
fill='url(#findymail_paint0)'
|
||||
fill={`url(#${gradient0})`}
|
||||
/>
|
||||
<path
|
||||
fillRule='evenodd'
|
||||
clipRule='evenodd'
|
||||
d='M42.3815 27.2995C42.3815 27.8571 41.9312 28.3074 41.3737 28.3074H4.0725C3.51497 28.3074 3.06644 27.8571 3.06644 27.2995V7.29083C3.06644 6.39914 4.1279 5.9381 4.77835 6.54745L9.53884 11.0042C9.95341 11.3937 10.6182 11.1971 10.7718 10.6485C10.8791 10.2715 11.0167 9.90338 11.1846 9.54956C11.4062 9.08138 11.2579 8.51848 10.8791 8.16466L7.1854 4.70509C6.52958 4.09037 6.96382 2.9896 7.86445 2.9896H37.58C38.4806 2.9896 38.9166 4.09216 38.259 4.70688L34.5671 8.16109C34.1901 8.51491 34.04 9.07959 34.2633 9.54777C34.4295 9.90159 34.5671 10.2679 34.6743 10.645C34.828 11.1918 35.4928 11.3884 35.9073 11.0006L40.6678 6.54567C41.3183 5.93631 42.3815 6.39735 42.3815 7.28726V27.2995ZM42.9141 0H2.53213C1.13294 0 0 1.13294 0 2.53213V28.7631C0 30.1622 1.13294 31.2952 2.53213 31.2952H42.9141C44.3132 31.2952 45.4462 30.1622 45.4462 28.7631V2.53213C45.4462 1.13294 44.3132 0 42.9141 0Z'
|
||||
fill='url(#findymail_paint1)'
|
||||
fill={`url(#${gradient1})`}
|
||||
/>
|
||||
<defs>
|
||||
<linearGradient
|
||||
id='findymail_paint0'
|
||||
id={gradient0}
|
||||
x1='20.5769'
|
||||
y1='8.68821'
|
||||
x2='24.6922'
|
||||
@@ -2324,7 +2342,7 @@ export function FindymailIcon(props: SVGProps<SVGSVGElement>) {
|
||||
<stop offset='1' stopColor='#B91C1C' />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id='findymail_paint1'
|
||||
id={gradient1}
|
||||
x1='22.7231'
|
||||
y1='0'
|
||||
x2='22.7231'
|
||||
@@ -3984,14 +4002,7 @@ export const SMSIcon = (props: SVGProps<SVGSVGElement>) => (
|
||||
)
|
||||
|
||||
export const ResendIcon = (props: SVGProps<SVGSVGElement>) => (
|
||||
<svg
|
||||
{...props}
|
||||
width='1800'
|
||||
height='1800'
|
||||
viewBox='0 0 1800 1800'
|
||||
fill='none'
|
||||
xmlns='http://www.w3.org/2000/svg'
|
||||
>
|
||||
<svg {...props} viewBox='360 360 1080 1080' fill='none' xmlns='http://www.w3.org/2000/svg'>
|
||||
<path
|
||||
d='M1000.46 450C1174.77 450 1278.43 553.67 1278.43 691.28C1278.43 828.9 1174.77 932.56 1000.46 932.56H912.38L1350 1350H1040.82L707.79 1033.48C683.94 1011.47 672.94 985.78 672.94 963.77C672.94 932.57 694.96 905.05 737.16 893.12L908.71 847.24C973.85 829.81 1018.81 779.35 1018.81 713.3C1018.8 632.57 952.75 585.78 871.1 585.78H450V450H1000.46Z'
|
||||
fill='#FDFDFD'
|
||||
@@ -4455,6 +4466,20 @@ export function VllmIcon(props: SVGProps<SVGSVGElement>) {
|
||||
)
|
||||
}
|
||||
|
||||
export function LitellmIcon(props: SVGProps<SVGSVGElement>) {
|
||||
return (
|
||||
<svg {...props} fill='none' viewBox='0 0 72 72' xmlns='http://www.w3.org/2000/svg'>
|
||||
<title>LiteLLM</title>
|
||||
<image
|
||||
href='data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEgAAABICAMAAABiM0N1AAAC+lBMVEUAAABmZmXo6uvl5uQ+S3bd4uPl5t5DRk/n6ed6nbji4tYvPmuosLyIqcOApcAnJB86Rm8xLSpgc5Oguszi49zs7eakqaPq6uRNXY7T08M5SYc0QGsoJiRRT0mFhXZ0d3CnuMRHS1LZ2cr4+PIoNGM0MyslIR9KV341Mi0qO22WnrrD0tuLkqBpj7AqM1STrMdlZU+amXyfn4SyxtPn6ONOWXzj49urwtLs7OOeoZ/F09xMX5C6yNY0NkPDytJ5eWBteqQvQGxndpGarMBGRDs0MyqlpY5LSD+RkXZqc48wPWTZ2dGtraZETnHX18xXV0YhHxt9fnSvtclUaZ87OjFvb1f4+PJ5eV9zc1vk5Nj8/Pbq6t/m5trt7eRoaFL19u/i4tXd3c/6+vTz8+zv7+dsbFba2szT08Po6Nzf39J2dl3s7OHW1scmNWM4RnksPnR+fmNkZE7x8elml7rNzbw8SX2BgWWIiGwmNmcrPHAhLFNlZVDCwq1MX6ArO2soNlwnN2zQ0MBEW6VQVVhEQTk2MSyjo4YxQXdwnsDLy7nGxrJFXas2SIQdJ0fX18saIj1IRzsuKiWxz+amx+GQt9Z3pMZLZbtIYbO7u6RBV6A9UpWnjF+Ks9Fgk7itrZI2SYk+ToQwQXyWl3kzQG6FhWl7qMlpmr+7vaq+vqi2tqI7TYxHVIQvO2ZNVV6fwdtThatJXJqyspkzRYB+gHiPj3FUWV0dGxeDrMvNz8fIyLY/VZs6TpFEVY6bmnxUaHqSlHg8OTSQmrQ4RXI8RGGZvNdxjqZSZqRGWZXGycC9wb2xt7NmeK6BlKRjhKNOWX5+gXt7fXZPWHNsgL9Ra7yirrKqqp9RY56kpJOuk2A0PVXqq1RKS0ZTU0Nxmrhug55kfZVCUolJUmxfYFpCRUaTs85ajrR2krGUpa1LZK2RnaRaWkzppUXa29h6jMWFo7XAwbRaia5ecH6rkmMqMkg2Okayu9N2h5KUloeXiWRqamBFU3qbhFjglzLFgynBltUQAAAAVXRSTlMACgUOhiGTMS/98YYc/v7+h4X+29dmU0n59PPy2Rz9/fr59fPz7urMoZ2Zgzn++/jz8tC/tKmooH9xZl1VTPv079zZ08q1m5h+dV3l49/U0rGreG9fuSw7vQAABwlJREFUWMPs0z+M0lAcwHFzBHIQYRCwYSM6KrkBIoRcHHTUOJJU2tAW0j+hU+vowlaXDnIkDmU8DAuO4OJEk0JggzABywFx4WC52d/j8eeiIODiwjcBpvfh995rH5w6derU/+rMZrefE4TfH4s5oH8ybHbCH3sR8flcLhfL8rosC4HjB7ETjojPxfKykMt9WPfp2FkIx7uAAEBO5lmJISkxmc3SUPJI6Pnbbq/X63a75a+339l0Kg0pipLJZI/b1sPHr+bzH6vmZUqkcKTrmMO5jL52hmbfNg2b4tIB6FAlHvV6NLUIqWppmWpaFAkxjHQYZLuMet1IUUuaVoCucQWtZUmSBE8A/+QAxh4PX3hUFRAQqlXDMK6WGUZ1kuN59CDthc7On3ndfaQAAkS9XqlU8rjKlVH1dAQZEgL7GY9aVJECCBCfF+UrN27nONSGzKYA7YHWDFKWCCLM9vARNBwMzFarkxNgqL9B9g2DlXx9YQzv7lo/O82GVavpus6zDMq1+6biYfeGyddvPOP2YNCaNBs1XYJXQ0zisjhq1+EQ4Qt1VCxp13BFaDMmDNGo8QyFltK41W8GonccjiPoHI0WZ1zojxcGS4rr/8eLlXQ6lUhwqETi6dZd+X1BGEfVNK3vnDQtnbm3ExoAWI/i0FcKSgC07cojL6ezGbwJ/WmnsToP+ICBCQ73XuRZHiVTAG3Z1ZvGxAz1ndOgxTMkBQwgyMAIAKtYGk+WygL0566Ej+Uvt5YFd8OQJAUQIDAJNu7HMSmOQ046qSj0b+P8ar7OQ5oM4wCOv7NjfySoUSJ5ZXhGdEFBdN8HRKdtYpnSYW7tSFrNpducW1LOzH90M8i5BelaUOug3Ky1wKiBHdJsFeEBYakEVnRDv+f3vDu0Wf/2wcF42fN9n+d5977MhTNuGwylRwsPF0AGOrlkR7ASUthLHmZwoQqFxQDe5BzanTNid6ZOKW9uKT2FGViVECvBmcD54fT7Pw8NPSM8k++x3no8K0K/yRNnnDY0nyo8QzI4F7y8/sLefcX79whzhZ876+rq6uurqjSmsvb29jLC2TgtdFlHmw2nyyFTlBOoELvYBFw6MPisDktVGhryer1lXqc1EIpYsvpRS2n54YKCohwh7i6CCDYOEfhQnQMV2jGayvysen9o3PYVHc9PHT4IGbxIezECEyEJGM7Kz1/WCRVcmLFVdqWs7AoRDEXOnCy59BAzxbDBAGYizGUT+Sx4qh4cqqcZ6DSIH0DkAbhisk9jO3FNjna4nfbg3mAkkChCBdT0/ipAMq0NDreTZJzObqesUozrmhlnb61qv5WLN2LxnlwsBIcfBPTJfMujAUaYTYPD2mgxOaHSbTJ1iyXx2HE2GqvqPYPkaZC7Ox8DdCiMRYWse0bQSjONepsMIiaZTGaqVEIoYlEc6XR6BoWH8ougcCY4tBzhjwTikbcBOBwOq7VRb29SiWUyMXDLLPJUhomlnU7PrQIcHzLyNnGaKgVvHFZIwFz0kKmUKCwQcbstFrFSlMpMWKVv1fT392u8H3B0YGgpamYZwDsrJuz2pibIlCgVNguyuZWqVGaj3mHUaOAaxLWwYy8GNRtaiKvofAcESEIiKSlRq+WiapUNiSwixWwmSw/LJqe69BxO2mLoOhfi5NXzQV/udBBPqZ6enk+++1RPz/0UJstuorP93kXOevXrr4GXaGBg4OePrpMBbUmPwbWgJ73JcOAJSk5mspSSykoyWfcX+vmv54aHh1+gc20n25AZHJitUilCVFfX1FSjmtmKmhpmkkqhEhH3k8zgrrnNfNfPbL4ecDy79wargkpx9fX1vSIqUvpSGO5KrCoUvuTjI11gHQNSqfTYaNI8voAnEPB4Ani/nGEWx8McwcdvPGkYPCAA/D/l8XlIIOVn8xkorVRgqDebzxcgHkWHZwcd+AN7OC9vLgNmZVgU1RCCw4j2+HRg3t+cOAF/CEIgckeGW+X7JuCFEECHfjjEkTFBiKYWL8hISgru8TEpj6wJXv8QWGPob+hF69bNnz9/3rx55uvHpbhHuE2j4SaOIlg++j+eyMhZsbGxi7b1Vly+DF+ZywEVfu/Ba3AWaMlLe1a7ZcMGJixuhkokVyuBXC5CKj+fVqt1uXS62qioqJt+WznhO5wFNrlaXUJvcoQx7H0is1iTuX5NGsTQzaiba8cIbYzHjkQCIXVISC6ykY5rfSInghNTqwOkpouKCd+ZtZl2SAkog2w+l/aVNo2Ly0+vxSXqXLXp3DEWxnYqgZglQ+4+2GRXDF1ITJROS8CEIsKGlqZiiJZCM2KxD65QWmYie8KYdJiOrjYdwuFwstQlADOkYLoUEJ+QlrB2Z2AYZ2lmQkJC5tIwHcTdNImKjo4eHxANNnG53BGjOImJiRzmP/Ubf4ltRM+YtyIAAAAASUVORK5CYII='
|
||||
width='72'
|
||||
height='72'
|
||||
preserveAspectRatio='xMidYMid meet'
|
||||
/>
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
|
||||
export function PosthogIcon(props: SVGProps<SVGSVGElement>) {
|
||||
return (
|
||||
<svg
|
||||
@@ -4650,17 +4675,40 @@ export function ZendeskIcon(props: SVGProps<SVGSVGElement>) {
|
||||
}
|
||||
|
||||
export function ZoomIcon(props: SVGProps<SVGSVGElement>) {
|
||||
return (
|
||||
<svg {...props} viewBox='0 0 24 24' xmlns='http://www.w3.org/2000/svg' aria-hidden='true'>
|
||||
<rect x='2' y='6' width='14' height='12' rx='2.5' fill='#0B5CFF' />
|
||||
<path d='M16 9 L22 6 V18 L16 15 Z' fill='#0B5CFF' />
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
|
||||
export function ZoomInfoIcon(props: SVGProps<SVGSVGElement>) {
|
||||
const id = useId()
|
||||
const clipId = `zoominfo-clip_${id}`
|
||||
return (
|
||||
<svg
|
||||
{...props}
|
||||
fill='currentColor'
|
||||
width='800px'
|
||||
height='800px'
|
||||
viewBox='-1 9.5 34 13'
|
||||
version='1.1'
|
||||
viewBox='0 0 65 65'
|
||||
fill='none'
|
||||
xmlns='http://www.w3.org/2000/svg'
|
||||
aria-hidden='true'
|
||||
>
|
||||
<path d='M19.28 17.4c-0.37 0.37-0.88 0.61-1.44 0.61-1.12 0-2.02-0.91-2.02-2.02s0.91-2.02 2.02-2.02c0.93 0 1.71 0.63 1.95 1.48l0 0.01c0.05 0.16 0.07 0.34 0.07 0.53 0 0.55-0.22 1.05-0.58 1.42l0-0zM15.47 13.59c-0.65 0.62-1.05 1.48-1.05 2.45 0 1.86 1.51 3.37 3.37 3.37s3.37-1.51 3.37-3.37c0-1.54-1.03-2.84-2.44-3.24l-0.02-0.01c-0.27-0.08-0.58-0.12-0.9-0.12-0.9 0-1.72 0.35-2.32 0.93l00zM28.3 12.6c-0.8 0-1.52 0.35-2.02 0.91l0 0c-0.5-0.56-1.22-0.91-2.02-0.91-0.56 0-1.09 0.17-1.52 0.47l0.01-0.01c-0.32-0.28-0.73-0.45-1.18-0.46l0-0v6.74l0.34-0.02c0.54-0.01 0.98-0.45 1-0.99l00 0.02-0.34v-2.36l0.02-0.34c00 00 00 0-0.24 0.06-0.48 0.17-0.68l0 0.01c0.24-0.4 0.67-0.67 1.17-0.67s0.93 0.27 1.16 0.66l0 0.01c0.1 0.2 0.16 0.43 0.17 0.68v0l0.02 0.34v2.36l0.02 0.34c0.02 0.54 0.45 0.97 0.99 1l0 0 0.34 0.02v-3.71l0.02-0.34c00 00 00 0-0.25 0.06-0.48 0.17-0.68l0 0.01c0.24-0.4 0.67-0.67 1.17-0.67 0.5 0 0.93 0.27 1.16 0.67l0 0.01c0.1 0.2 0.16 0.43 0.16 0.67 0 0 0 0 0 0v-0l0.02 0.34v2.36l0.02 0.34c0.02 0.54 0.45 0.98 0.99 1l0 0 0.34 0.02v-4.04c0-1.49-1.21-2.69-2.7-2.690 00 00 0h0zM12.21 17.4c-0.37 0.39-0.89 0.64-1.47 0.64-1.12 0-2.02-0.91-2.02-2.02s0.91-2.02 2.02-2.02c0.92 0 1.7 0.62 1.95 1.47l0 0.01c0.05 0.16 0.08 0.35 0.08 0.54 0 0.54-0.21 1.02-0.55 1.39l00zM10.78 12.6h0c-1.86 0-3.37 1.51-3.37 3.37s1.51 3.37 3.37 3.37 3.37-1.51 3.37-3.37c0-1.86-1.51-3.37-3.37-3.37h-0zM6.73 18.01l-0.34-0.01h-3.03l4.04-4.04-0.02-0.34c-0.01-0.54-0.45-0.98-0.99-0.99l0-0-0.34-0.02h-5.05l0.02 0.34c0.03 0.54 0.46 0.97 0.99 1l0 0 0.34 0.02h3.04l-4.05 4.05 0.02 0.34c0.02 0.54 0.45 0.98 0.99 1l0 0 0.34 0.02h5.06l-0.02-0.34c-0.02-0.54-0.45-0.97-0.99-0.99l0-0z' />
|
||||
<g clipPath={`url(#${clipId})`}>
|
||||
<path
|
||||
d='M0.272461 8.27246C0.272461 3.85418 3.85418 0.272461 8.27246 0.272461H56.2725C60.6907 0.272461 64.2725 3.85418 64.2725 8.27246V56.2725C64.2725 60.6907 60.6907 64.2725 56.2725 64.2725H8.27246C3.85418 64.2725 0.272461 60.6907 0.272461 56.2725V8.27246Z'
|
||||
fill='#EA1B15'
|
||||
/>
|
||||
<path
|
||||
d='M44.2725 16.2725H47.7096C48.0212 16.2725 48.2726 16.5239 48.2726 16.8356V25.6993C48.2726 26.011 48.0313 26.2624 47.7196 26.2725H44.8355C44.5239 26.2725 44.2725 26.0211 44.2725 25.7093V23.1162L22.0381 44.2725H27.7095C28.0212 44.2725 28.2725 44.524 28.2725 44.8357V47.7094C28.2725 48.0211 28.0212 48.2726 27.7095 48.2726H16.8355C16.5238 48.2726 16.2725 48.0211 16.2725 47.7094V44.2725L41.4757 20.2725H38.8355C38.5239 20.2725 38.2725 20.0211 38.2725 19.7093V16.8457C38.2725 16.5339 38.5239 16.2825 38.8355 16.2825L44.2725 16.2725ZM24.2725 20.2725L33.7095 20.2624C34.0212 20.2624 34.2725 20.011 34.2725 19.6992V16.8356C34.2725 16.5239 34.0212 16.2725 33.7196 16.2725H16.8556C16.5439 16.2725 16.2926 16.5239 16.2926 16.8356V25.7093C16.2926 26.0211 16.5439 26.2725 16.8556 26.2725H19.7195C20.0312 26.2725 20.2725 26.0211 20.2725 25.7093V20.2725H24.2725ZM44.2725 44.2725H32.8355C32.5238 44.2725 32.2725 44.524 32.2725 44.8357V47.7094C32.2725 48.0211 32.5238 48.2726 32.8355 48.2726H47.7096C48.0212 48.2726 48.2726 48.0211 48.2726 47.7094V38.7888C48.2726 38.4771 48.0212 38.2257 47.7096 38.2257H44.8355C44.5239 38.2257 44.2725 38.4771 44.2725 38.7888V44.2725Z'
|
||||
fill='white'
|
||||
/>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id={clipId}>
|
||||
<rect width='64' height='64' fill='white' transform='translate(0.272461 0.272461)' />
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -209,6 +209,7 @@ import {
|
||||
ZendeskIcon,
|
||||
ZepIcon,
|
||||
ZoomIcon,
|
||||
ZoomInfoIcon,
|
||||
} from '@/components/icons'
|
||||
|
||||
type IconComponent = ComponentType<SVGProps<SVGSVGElement>>
|
||||
@@ -448,4 +449,5 @@ export const blockTypeToIconMap: Record<string, IconComponent> = {
|
||||
zendesk: ZendeskIcon,
|
||||
zep: ZepIcon,
|
||||
zoom: ZoomIcon,
|
||||
zoominfo: ZoomInfoIcon,
|
||||
}
|
||||
|
||||
@@ -262,6 +262,8 @@ Get details of a specific email message in AgentMail
|
||||
| `subject` | string | Message subject |
|
||||
| `text` | string | Plain text content |
|
||||
| `html` | string | HTML content |
|
||||
| `labels` | array | Labels assigned to the message |
|
||||
| `timestamp` | string | Time the message was sent or drafted |
|
||||
| `createdAt` | string | Creation timestamp |
|
||||
|
||||
### `agentmail_get_thread`
|
||||
@@ -298,6 +300,8 @@ Get details of a specific email thread including messages in AgentMail
|
||||
| ↳ `subject` | string | Message subject |
|
||||
| ↳ `text` | string | Plain text content |
|
||||
| ↳ `html` | string | HTML content |
|
||||
| ↳ `labels` | array | Labels assigned to the message |
|
||||
| ↳ `timestamp` | string | Time the message was sent or drafted |
|
||||
| ↳ `createdAt` | string | Creation timestamp |
|
||||
|
||||
### `agentmail_list_drafts`
|
||||
@@ -380,6 +384,7 @@ List messages in an inbox in AgentMail
|
||||
| ↳ `to` | array | Recipient email addresses |
|
||||
| ↳ `subject` | string | Message subject |
|
||||
| ↳ `preview` | string | Message preview text |
|
||||
| ↳ `timestamp` | string | Time the message was sent or drafted |
|
||||
| ↳ `createdAt` | string | Creation timestamp |
|
||||
| `count` | number | Total number of messages |
|
||||
| `nextPageToken` | string | Token for retrieving the next page |
|
||||
|
||||
@@ -7,7 +7,7 @@ import { BlockInfoCard } from "@/components/ui/block-info-card"
|
||||
|
||||
<BlockInfoCard
|
||||
type="agentphone"
|
||||
color="linear-gradient(135deg, #1a1a1a 0%, #0a2a14 100%)"
|
||||
color="#000000"
|
||||
/>
|
||||
|
||||
{/* MANUAL-CONTENT-START:intro */}
|
||||
@@ -254,6 +254,7 @@ Get a conversation along with its recent messages
|
||||
| ↳ `direction` | string | inbound or outbound |
|
||||
| ↳ `channel` | string | sms, mms, or imessage |
|
||||
| ↳ `mediaUrl` | string | Attached media URL |
|
||||
| ↳ `mediaUrls` | array | All attached media URLs |
|
||||
| ↳ `receivedAt` | string | ISO 8601 timestamp |
|
||||
|
||||
### `agentphone_get_conversation_messages`
|
||||
@@ -282,6 +283,7 @@ Get paginated messages for a conversation
|
||||
| ↳ `direction` | string | inbound or outbound |
|
||||
| ↳ `channel` | string | sms, mms, or imessage |
|
||||
| ↳ `mediaUrl` | string | Attached media URL |
|
||||
| ↳ `mediaUrls` | array | All attached media URLs |
|
||||
| ↳ `receivedAt` | string | ISO 8601 timestamp |
|
||||
| `hasMore` | boolean | Whether more messages are available |
|
||||
|
||||
@@ -425,7 +427,7 @@ List contacts for this AgentPhone account
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `apiKey` | string | Yes | AgentPhone API key |
|
||||
| `search` | string | No | Filter by name or phone number \(case-insensitive contains\) |
|
||||
| `limit` | number | No | Number of results to return \(default 50\) |
|
||||
| `limit` | number | No | Number of results to return \(default 50, max 200\) |
|
||||
| `offset` | number | No | Number of results to skip \(min 0\) |
|
||||
|
||||
#### Output
|
||||
@@ -624,6 +626,7 @@ Update conversation metadata (stored state). Pass null to clear existing metadat
|
||||
| ↳ `direction` | string | inbound or outbound |
|
||||
| ↳ `channel` | string | Channel \(sms, mms, etc.\) |
|
||||
| ↳ `mediaUrl` | string | Media URL if any |
|
||||
| ↳ `mediaUrls` | array | All attached media URLs |
|
||||
| ↳ `receivedAt` | string | ISO 8601 timestamp |
|
||||
|
||||
|
||||
|
||||
@@ -179,7 +179,7 @@ Enrich data for up to 10 organizations at once using Apollo
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `apiKey` | string | Yes | Apollo API key |
|
||||
| `organizations` | array | Yes | Array of organizations to enrich \(max 10\). Each item requires `name` and may include `domain` \(e.g., \[\{"name": "Example Corp", "domain": "example.com"\}\]\) |
|
||||
| `domains` | array | Yes | Array of company domains to enrich \(max 10, no www. or @, e.g., \["apollo.io", "stripe.com"\]\) |
|
||||
|
||||
#### Output
|
||||
|
||||
@@ -325,8 +325,10 @@ Update up to 100 existing contacts at once in your Apollo database. Each contact
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `message` | string | Confirmation message from Apollo |
|
||||
| `job_id` | string | Async job ID \(returned for >100 contacts\) |
|
||||
| `contacts` | json | Updated contacts \(synchronous response, ≤100 contacts\) |
|
||||
| `entity_progress_job` | json | Async job descriptor \(>100 contacts or async=true\): \{id, status, ...\} |
|
||||
| `job_id` | string | Async job ID extracted from entity_progress_job |
|
||||
| `message` | string | Optional confirmation message from Apollo |
|
||||
|
||||
### `apollo_account_create`
|
||||
|
||||
@@ -445,8 +447,11 @@ Update up to 1000 existing accounts at once in your Apollo database (higher limi
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `message` | string | Confirmation message from Apollo |
|
||||
| `accounts` | json | Updated accounts \(synchronous response, ≤100 accounts\): \[\{id, account_stage_id, ...\}\] |
|
||||
| `account_ids` | json | IDs of accounts that were updated |
|
||||
| `entity_progress_job` | json | Async job descriptor \(>100 accounts or async=true\) |
|
||||
| `job_id` | string | Async job ID extracted from entity_progress_job |
|
||||
| `message` | string | Optional confirmation message from Apollo |
|
||||
|
||||
### `apollo_opportunity_create`
|
||||
|
||||
|
||||
@@ -373,7 +373,7 @@ Fetch full details of a single work item by ID from Azure DevOps, including titl
|
||||
|
||||
### `azure_devops_get_work_items_batch`
|
||||
|
||||
Fetch full details for multiple work items by ID from Azure DevOps. Pass comma-separated IDs (e.g.
|
||||
Fetch full details for multiple work items by ID from Azure DevOps. Pass comma-separated IDs (e.g.
|
||||
|
||||
#### Input
|
||||
|
||||
|
||||
@@ -171,7 +171,8 @@ Download a signed document from a completed DocuSign envelope
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `base64Content` | string | Base64-encoded document content |
|
||||
| `file` | file | Stored downloaded document file |
|
||||
| `base64Content` | string | Deprecated legacy inline content. New downloads return file. |
|
||||
| `mimeType` | string | MIME type of the document |
|
||||
| `fileName` | string | Original file name |
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
---
|
||||
title: ElevenLabs
|
||||
description: Convert TTS using ElevenLabs
|
||||
description: Convert text to speech with ElevenLabs
|
||||
---
|
||||
|
||||
import { BlockInfoCard } from "@/components/ui/block-info-card"
|
||||
@@ -35,7 +35,7 @@ Integrate ElevenLabs into the workflow. Can convert text to speech.
|
||||
|
||||
### `elevenlabs_tts`
|
||||
|
||||
Convert TTS using ElevenLabs voices
|
||||
Convert text to speech using ElevenLabs voices
|
||||
|
||||
#### Input
|
||||
|
||||
@@ -45,7 +45,7 @@ Convert TTS using ElevenLabs voices
|
||||
| `voiceId` | string | Yes | The ID of the voice to use \(e.g., "21m00Tcm4TlvDq8ikWAM" for Rachel\) |
|
||||
| `modelId` | string | No | The ID of the model to use \(e.g., "eleven_multilingual_v2", "eleven_turbo_v2"\). Defaults to eleven_monolingual_v1 |
|
||||
| `stability` | number | No | Voice stability setting from 0.0 to 1.0 \(e.g., 0.5 for balanced, 0.75 for more stable\). Higher values produce more consistent output |
|
||||
| `similarity` | number | No | Similarity boost setting from 0.0 to 1.0 \(e.g., 0.75 for natural, 1.0 for maximum similarity\). Higher values make the voice more similar to the original |
|
||||
| `similarityBoost` | number | No | Similarity boost setting from 0.0 to 1.0 \(e.g., 0.75 for natural, 1.0 for maximum similarity\). Higher values make the voice more similar to the original |
|
||||
| `apiKey` | string | Yes | Your ElevenLabs API key |
|
||||
|
||||
#### Output
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -26,7 +26,7 @@ In Sim, the HubSpot integration enables your agents to interact with your CRM da
|
||||
|
||||
## Usage Instructions
|
||||
|
||||
Integrate HubSpot into your workflow. Manage contacts, companies, deals, tickets, and other CRM objects with powerful automation capabilities. Can be used in trigger mode to start workflows when contacts are created, deleted, or updated.
|
||||
Integrate HubSpot into your workflow. Manage contacts, companies, deals, tickets, and other CRM objects with powerful automation capabilities. Can be used in trigger mode to start workflows when records are created, updated, a specific property changes, or a contact joins a list.
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -58,6 +58,9 @@ Generate images with OpenAI GPT Image, Google Nano Banana, or Fal.ai image model
|
||||
| `enableSafetyChecker` | boolean | No | Enable the Fal.ai safety checker when supported |
|
||||
| `enableWebSearch` | boolean | No | Enable web search grounding when supported by the selected Fal.ai model |
|
||||
| `thinkingLevel` | string | No | Fal.ai thinking level when supported: minimal or high |
|
||||
| `pricing` | custom | No | No description |
|
||||
| `metadata` | string | No | No description |
|
||||
| `rateLimit` | string | No | No description |
|
||||
|
||||
#### Output
|
||||
|
||||
|
||||
@@ -36,11 +36,15 @@ Retrieves Instantly V2 leads with search, campaign, list, and pagination filters
|
||||
| `items` | string | No | No description |
|
||||
| `excluded_ids` | array | No | Lead IDs to exclude |
|
||||
| `items` | string | No | No description |
|
||||
| `organization_user_ids` | array | No | Organization user IDs to filter leads |
|
||||
| `items` | string | No | No description |
|
||||
| `smart_view_id` | string | No | Smart view ID to filter leads |
|
||||
| `contacts` | array | No | Lead email addresses to include |
|
||||
| `items` | string | No | No description |
|
||||
| `limit` | number | No | Number of leads to return, from 1 to 100 |
|
||||
| `starting_after` | string | No | Forward pagination cursor from next_starting_after |
|
||||
| `distinct_contacts` | boolean | No | Whether to return distinct contacts |
|
||||
| `is_website_visitor` | boolean | No | Whether the lead is a website visitor |
|
||||
| `enrichment_status` | number | No | Enrichment status filter |
|
||||
| `esg_code` | string | No | Email security gateway code filter |
|
||||
|
||||
@@ -48,14 +52,14 @@ Retrieves Instantly V2 leads with search, campaign, list, and pagination filters
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `leads` | array | List of leads |
|
||||
| `lead` | json | Lead details |
|
||||
| `campaigns` | array | List of campaigns |
|
||||
| `campaign` | json | Campaign details |
|
||||
| `emails` | array | List of emails |
|
||||
| `email` | json | Email details |
|
||||
| `lead_lists` | array | List of lead lists |
|
||||
| `lead_list` | json | Lead list details |
|
||||
| `leads` | array | List of leads \(id, email, first_name, last_name, campaign, status\) |
|
||||
| `lead` | json | Lead details \(id, email, first_name, last_name, company_name, job_title, campaign, status, payload\) |
|
||||
| `campaigns` | array | List of campaigns \(id, name, status, daily_limit\) |
|
||||
| `campaign` | json | Campaign details \(id, name, status, daily_limit, daily_max_leads, open_tracking\) |
|
||||
| `emails` | array | List of emails \(id, subject, from_address_email, lead, thread_id\) |
|
||||
| `email` | json | Email details \(id, subject, from_address_email, to_address_email_list, thread_id, content_preview\) |
|
||||
| `lead_lists` | array | List of lead lists \(id, name, has_enrichment_task, timestamp_created\) |
|
||||
| `lead_list` | json | Lead list details \(id, organization_id, has_enrichment_task, owned_by, name, timestamp_created\) |
|
||||
| `count` | number | Returned or affected record count |
|
||||
| `next_starting_after` | string | Cursor for the next page |
|
||||
| `id` | string | Record ID |
|
||||
@@ -82,14 +86,14 @@ Retrieves an Instantly V2 lead by ID.
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `leads` | array | List of leads |
|
||||
| `lead` | json | Lead details |
|
||||
| `campaigns` | array | List of campaigns |
|
||||
| `campaign` | json | Campaign details |
|
||||
| `emails` | array | List of emails |
|
||||
| `email` | json | Email details |
|
||||
| `lead_lists` | array | List of lead lists |
|
||||
| `lead_list` | json | Lead list details |
|
||||
| `leads` | array | List of leads \(id, email, first_name, last_name, campaign, status\) |
|
||||
| `lead` | json | Lead details \(id, email, first_name, last_name, company_name, job_title, campaign, status, payload\) |
|
||||
| `campaigns` | array | List of campaigns \(id, name, status, daily_limit\) |
|
||||
| `campaign` | json | Campaign details \(id, name, status, daily_limit, daily_max_leads, open_tracking\) |
|
||||
| `emails` | array | List of emails \(id, subject, from_address_email, lead, thread_id\) |
|
||||
| `email` | json | Email details \(id, subject, from_address_email, to_address_email_list, thread_id, content_preview\) |
|
||||
| `lead_lists` | array | List of lead lists \(id, name, has_enrichment_task, timestamp_created\) |
|
||||
| `lead_list` | json | Lead list details \(id, organization_id, has_enrichment_task, owned_by, name, timestamp_created\) |
|
||||
| `count` | number | Returned or affected record count |
|
||||
| `next_starting_after` | string | Cursor for the next page |
|
||||
| `id` | string | Record ID |
|
||||
@@ -127,6 +131,7 @@ Creates an Instantly V2 lead in a campaign or lead list.
|
||||
| `skip_if_in_campaign` | boolean | No | Skip if the lead already exists in the campaign |
|
||||
| `skip_if_in_list` | boolean | No | Skip if the lead already exists in the list |
|
||||
| `blocklist_id` | string | No | Blocklist ID to check for the lead |
|
||||
| `verify_leads_for_lead_finder` | boolean | No | Whether to verify leads imported from Lead Finder |
|
||||
| `verify_leads_on_import` | boolean | No | Whether to verify leads on import |
|
||||
| `custom_variables` | json | No | Custom variable object with string, number, boolean, or null values |
|
||||
|
||||
@@ -134,14 +139,14 @@ Creates an Instantly V2 lead in a campaign or lead list.
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `leads` | array | List of leads |
|
||||
| `lead` | json | Lead details |
|
||||
| `campaigns` | array | List of campaigns |
|
||||
| `campaign` | json | Campaign details |
|
||||
| `emails` | array | List of emails |
|
||||
| `email` | json | Email details |
|
||||
| `lead_lists` | array | List of lead lists |
|
||||
| `lead_list` | json | Lead list details |
|
||||
| `leads` | array | List of leads \(id, email, first_name, last_name, campaign, status\) |
|
||||
| `lead` | json | Lead details \(id, email, first_name, last_name, company_name, job_title, campaign, status, payload\) |
|
||||
| `campaigns` | array | List of campaigns \(id, name, status, daily_limit\) |
|
||||
| `campaign` | json | Campaign details \(id, name, status, daily_limit, daily_max_leads, open_tracking\) |
|
||||
| `emails` | array | List of emails \(id, subject, from_address_email, lead, thread_id\) |
|
||||
| `email` | json | Email details \(id, subject, from_address_email, to_address_email_list, thread_id, content_preview\) |
|
||||
| `lead_lists` | array | List of lead lists \(id, name, has_enrichment_task, timestamp_created\) |
|
||||
| `lead_list` | json | Lead list details \(id, organization_id, has_enrichment_task, owned_by, name, timestamp_created\) |
|
||||
| `count` | number | Returned or affected record count |
|
||||
| `next_starting_after` | string | Cursor for the next page |
|
||||
| `id` | string | Record ID |
|
||||
@@ -184,7 +189,7 @@ Submits an Instantly V2 background job to update a lead interest status.
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `lead_email` | string | Yes | Lead email address |
|
||||
| `interest_value` | number | Yes | Interest status value. Use null in JSON/tool input to reset to Lead. |
|
||||
| `interest_value` | number | No | Interest status value. Leave empty in the block or pass null to reset to Lead. |
|
||||
| `campaign_id` | string | No | Campaign ID for the lead |
|
||||
| `list_id` | string | No | Lead list ID for the lead |
|
||||
| `ai_interest_value` | number | No | AI interest value to set for the lead |
|
||||
@@ -215,14 +220,14 @@ Retrieves Instantly V2 campaigns with search, status, tag, and pagination filter
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `leads` | array | List of leads |
|
||||
| `lead` | json | Lead details |
|
||||
| `campaigns` | array | List of campaigns |
|
||||
| `campaign` | json | Campaign details |
|
||||
| `emails` | array | List of emails |
|
||||
| `email` | json | Email details |
|
||||
| `lead_lists` | array | List of lead lists |
|
||||
| `lead_list` | json | Lead list details |
|
||||
| `leads` | array | List of leads \(id, email, first_name, last_name, campaign, status\) |
|
||||
| `lead` | json | Lead details \(id, email, first_name, last_name, company_name, job_title, campaign, status, payload\) |
|
||||
| `campaigns` | array | List of campaigns \(id, name, status, daily_limit\) |
|
||||
| `campaign` | json | Campaign details \(id, name, status, daily_limit, daily_max_leads, open_tracking\) |
|
||||
| `emails` | array | List of emails \(id, subject, from_address_email, lead, thread_id\) |
|
||||
| `email` | json | Email details \(id, subject, from_address_email, to_address_email_list, thread_id, content_preview\) |
|
||||
| `lead_lists` | array | List of lead lists \(id, name, has_enrichment_task, timestamp_created\) |
|
||||
| `lead_list` | json | Lead list details \(id, organization_id, has_enrichment_task, owned_by, name, timestamp_created\) |
|
||||
| `count` | number | Returned or affected record count |
|
||||
| `next_starting_after` | string | Cursor for the next page |
|
||||
| `id` | string | Record ID |
|
||||
@@ -262,14 +267,14 @@ Creates an Instantly V2 campaign using the documented campaign schedule schema.
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `leads` | array | List of leads |
|
||||
| `lead` | json | Lead details |
|
||||
| `campaigns` | array | List of campaigns |
|
||||
| `campaign` | json | Campaign details |
|
||||
| `emails` | array | List of emails |
|
||||
| `email` | json | Email details |
|
||||
| `lead_lists` | array | List of lead lists |
|
||||
| `lead_list` | json | Lead list details |
|
||||
| `leads` | array | List of leads \(id, email, first_name, last_name, campaign, status\) |
|
||||
| `lead` | json | Lead details \(id, email, first_name, last_name, company_name, job_title, campaign, status, payload\) |
|
||||
| `campaigns` | array | List of campaigns \(id, name, status, daily_limit\) |
|
||||
| `campaign` | json | Campaign details \(id, name, status, daily_limit, daily_max_leads, open_tracking\) |
|
||||
| `emails` | array | List of emails \(id, subject, from_address_email, lead, thread_id\) |
|
||||
| `email` | json | Email details \(id, subject, from_address_email, to_address_email_list, thread_id, content_preview\) |
|
||||
| `lead_lists` | array | List of lead lists \(id, name, has_enrichment_task, timestamp_created\) |
|
||||
| `lead_list` | json | Lead list details \(id, organization_id, has_enrichment_task, owned_by, name, timestamp_created\) |
|
||||
| `count` | number | Returned or affected record count |
|
||||
| `next_starting_after` | string | Cursor for the next page |
|
||||
| `id` | string | Record ID |
|
||||
@@ -310,14 +315,14 @@ Updates documented Instantly V2 campaign fields.
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `leads` | array | List of leads |
|
||||
| `lead` | json | Lead details |
|
||||
| `campaigns` | array | List of campaigns |
|
||||
| `campaign` | json | Campaign details |
|
||||
| `emails` | array | List of emails |
|
||||
| `email` | json | Email details |
|
||||
| `lead_lists` | array | List of lead lists |
|
||||
| `lead_list` | json | Lead list details |
|
||||
| `leads` | array | List of leads \(id, email, first_name, last_name, campaign, status\) |
|
||||
| `lead` | json | Lead details \(id, email, first_name, last_name, company_name, job_title, campaign, status, payload\) |
|
||||
| `campaigns` | array | List of campaigns \(id, name, status, daily_limit\) |
|
||||
| `campaign` | json | Campaign details \(id, name, status, daily_limit, daily_max_leads, open_tracking\) |
|
||||
| `emails` | array | List of emails \(id, subject, from_address_email, lead, thread_id\) |
|
||||
| `email` | json | Email details \(id, subject, from_address_email, to_address_email_list, thread_id, content_preview\) |
|
||||
| `lead_lists` | array | List of lead lists \(id, name, has_enrichment_task, timestamp_created\) |
|
||||
| `lead_list` | json | Lead list details \(id, organization_id, has_enrichment_task, owned_by, name, timestamp_created\) |
|
||||
| `count` | number | Returned or affected record count |
|
||||
| `next_starting_after` | string | Cursor for the next page |
|
||||
| `id` | string | Record ID |
|
||||
@@ -344,14 +349,14 @@ Activates, starts, or resumes an Instantly V2 campaign.
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `leads` | array | List of leads |
|
||||
| `lead` | json | Lead details |
|
||||
| `campaigns` | array | List of campaigns |
|
||||
| `campaign` | json | Campaign details |
|
||||
| `emails` | array | List of emails |
|
||||
| `email` | json | Email details |
|
||||
| `lead_lists` | array | List of lead lists |
|
||||
| `lead_list` | json | Lead list details |
|
||||
| `leads` | array | List of leads \(id, email, first_name, last_name, campaign, status\) |
|
||||
| `lead` | json | Lead details \(id, email, first_name, last_name, company_name, job_title, campaign, status, payload\) |
|
||||
| `campaigns` | array | List of campaigns \(id, name, status, daily_limit\) |
|
||||
| `campaign` | json | Campaign details \(id, name, status, daily_limit, daily_max_leads, open_tracking\) |
|
||||
| `emails` | array | List of emails \(id, subject, from_address_email, lead, thread_id\) |
|
||||
| `email` | json | Email details \(id, subject, from_address_email, to_address_email_list, thread_id, content_preview\) |
|
||||
| `lead_lists` | array | List of lead lists \(id, name, has_enrichment_task, timestamp_created\) |
|
||||
| `lead_list` | json | Lead list details \(id, organization_id, has_enrichment_task, owned_by, name, timestamp_created\) |
|
||||
| `count` | number | Returned or affected record count |
|
||||
| `next_starting_after` | string | Cursor for the next page |
|
||||
| `id` | string | Record ID |
|
||||
@@ -380,21 +385,20 @@ Retrieves Instantly V2 Unibox emails with search and pagination filters.
|
||||
| `i_status` | number | No | Email interest status filter |
|
||||
| `eaccount` | string | No | Sending email account filter |
|
||||
| `lead` | string | No | Lead email address filter |
|
||||
| `lead_id` | string | No | Lead ID filter |
|
||||
| `is_unread` | number | No | Unread status filter |
|
||||
| `is_unread` | boolean | No | Unread status filter |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `leads` | array | List of leads |
|
||||
| `lead` | json | Lead details |
|
||||
| `campaigns` | array | List of campaigns |
|
||||
| `campaign` | json | Campaign details |
|
||||
| `emails` | array | List of emails |
|
||||
| `email` | json | Email details |
|
||||
| `lead_lists` | array | List of lead lists |
|
||||
| `lead_list` | json | Lead list details |
|
||||
| `leads` | array | List of leads \(id, email, first_name, last_name, campaign, status\) |
|
||||
| `lead` | json | Lead details \(id, email, first_name, last_name, company_name, job_title, campaign, status, payload\) |
|
||||
| `campaigns` | array | List of campaigns \(id, name, status, daily_limit\) |
|
||||
| `campaign` | json | Campaign details \(id, name, status, daily_limit, daily_max_leads, open_tracking\) |
|
||||
| `emails` | array | List of emails \(id, subject, from_address_email, lead, thread_id\) |
|
||||
| `email` | json | Email details \(id, subject, from_address_email, to_address_email_list, thread_id, content_preview\) |
|
||||
| `lead_lists` | array | List of lead lists \(id, name, has_enrichment_task, timestamp_created\) |
|
||||
| `lead_list` | json | Lead list details \(id, organization_id, has_enrichment_task, owned_by, name, timestamp_created\) |
|
||||
| `count` | number | Returned or affected record count |
|
||||
| `next_starting_after` | string | Cursor for the next page |
|
||||
| `id` | string | Record ID |
|
||||
@@ -426,14 +430,14 @@ Sends an Instantly V2 reply to an existing Unibox email.
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `leads` | array | List of leads |
|
||||
| `lead` | json | Lead details |
|
||||
| `campaigns` | array | List of campaigns |
|
||||
| `campaign` | json | Campaign details |
|
||||
| `emails` | array | List of emails |
|
||||
| `email` | json | Email details |
|
||||
| `lead_lists` | array | List of lead lists |
|
||||
| `lead_list` | json | Lead list details |
|
||||
| `leads` | array | List of leads \(id, email, first_name, last_name, campaign, status\) |
|
||||
| `lead` | json | Lead details \(id, email, first_name, last_name, company_name, job_title, campaign, status, payload\) |
|
||||
| `campaigns` | array | List of campaigns \(id, name, status, daily_limit\) |
|
||||
| `campaign` | json | Campaign details \(id, name, status, daily_limit, daily_max_leads, open_tracking\) |
|
||||
| `emails` | array | List of emails \(id, subject, from_address_email, lead, thread_id\) |
|
||||
| `email` | json | Email details \(id, subject, from_address_email, to_address_email_list, thread_id, content_preview\) |
|
||||
| `lead_lists` | array | List of lead lists \(id, name, has_enrichment_task, timestamp_created\) |
|
||||
| `lead_list` | json | Lead list details \(id, organization_id, has_enrichment_task, owned_by, name, timestamp_created\) |
|
||||
| `count` | number | Returned or affected record count |
|
||||
| `next_starting_after` | string | Cursor for the next page |
|
||||
| `id` | string | Record ID |
|
||||
@@ -463,14 +467,14 @@ Retrieves Instantly V2 lead lists with search and pagination filters.
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `leads` | array | List of leads |
|
||||
| `lead` | json | Lead details |
|
||||
| `campaigns` | array | List of campaigns |
|
||||
| `campaign` | json | Campaign details |
|
||||
| `emails` | array | List of emails |
|
||||
| `email` | json | Email details |
|
||||
| `lead_lists` | array | List of lead lists |
|
||||
| `lead_list` | json | Lead list details |
|
||||
| `leads` | array | List of leads \(id, email, first_name, last_name, campaign, status\) |
|
||||
| `lead` | json | Lead details \(id, email, first_name, last_name, company_name, job_title, campaign, status, payload\) |
|
||||
| `campaigns` | array | List of campaigns \(id, name, status, daily_limit\) |
|
||||
| `campaign` | json | Campaign details \(id, name, status, daily_limit, daily_max_leads, open_tracking\) |
|
||||
| `emails` | array | List of emails \(id, subject, from_address_email, lead, thread_id\) |
|
||||
| `email` | json | Email details \(id, subject, from_address_email, to_address_email_list, thread_id, content_preview\) |
|
||||
| `lead_lists` | array | List of lead lists \(id, name, has_enrichment_task, timestamp_created\) |
|
||||
| `lead_list` | json | Lead list details \(id, organization_id, has_enrichment_task, owned_by, name, timestamp_created\) |
|
||||
| `count` | number | Returned or affected record count |
|
||||
| `next_starting_after` | string | Cursor for the next page |
|
||||
| `id` | string | Record ID |
|
||||
@@ -499,14 +503,14 @@ Creates an Instantly V2 lead list.
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `leads` | array | List of leads |
|
||||
| `lead` | json | Lead details |
|
||||
| `campaigns` | array | List of campaigns |
|
||||
| `campaign` | json | Campaign details |
|
||||
| `emails` | array | List of emails |
|
||||
| `email` | json | Email details |
|
||||
| `lead_lists` | array | List of lead lists |
|
||||
| `lead_list` | json | Lead list details |
|
||||
| `leads` | array | List of leads \(id, email, first_name, last_name, campaign, status\) |
|
||||
| `lead` | json | Lead details \(id, email, first_name, last_name, company_name, job_title, campaign, status, payload\) |
|
||||
| `campaigns` | array | List of campaigns \(id, name, status, daily_limit\) |
|
||||
| `campaign` | json | Campaign details \(id, name, status, daily_limit, daily_max_leads, open_tracking\) |
|
||||
| `emails` | array | List of emails \(id, subject, from_address_email, lead, thread_id\) |
|
||||
| `email` | json | Email details \(id, subject, from_address_email, to_address_email_list, thread_id, content_preview\) |
|
||||
| `lead_lists` | array | List of lead lists \(id, name, has_enrichment_task, timestamp_created\) |
|
||||
| `lead_list` | json | Lead list details \(id, organization_id, has_enrichment_task, owned_by, name, timestamp_created\) |
|
||||
| `count` | number | Returned or affected record count |
|
||||
| `next_starting_after` | string | Cursor for the next page |
|
||||
| `id` | string | Record ID |
|
||||
|
||||
@@ -207,6 +207,7 @@
|
||||
"youtube",
|
||||
"zendesk",
|
||||
"zep",
|
||||
"zoom"
|
||||
"zoom",
|
||||
"zoominfo"
|
||||
]
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@ description: Query observability data and record deployments in New Relic
|
||||
|
||||
import { BlockInfoCard } from "@/components/ui/block-info-card"
|
||||
|
||||
<BlockInfoCard
|
||||
<BlockInfoCard
|
||||
type="new_relic"
|
||||
color="#000000"
|
||||
/>
|
||||
@@ -141,3 +141,5 @@ Record a deployment change event in New Relic change tracking.
|
||||
| ↳ `guid` | string | Entity GUID |
|
||||
| ↳ `name` | string | Entity name |
|
||||
| `messages` | array | Messages returned by New Relic for the created change event |
|
||||
|
||||
|
||||
|
||||
@@ -124,5 +124,6 @@ Enrich company data with additional information and find associated people using
|
||||
| `structuredData` | json | Enriched company data matching the requested struct fields |
|
||||
| `references` | json | Source URLs and descriptions used for enrichment |
|
||||
| `confidenceScore` | number | Quality score for the returned data \(0-10\) |
|
||||
| `orgChart` | json | Org chart returned when fullOrgChart is enabled |
|
||||
|
||||
|
||||
|
||||
@@ -175,6 +175,9 @@ Generate videos using Fal.ai with access to Veo 3.1, Sora 2, Seedance 2.0, Kling
|
||||
| `resolution` | string | No | Video resolution \(varies by model\): 480p, 580p, 720p, 1080p, true_1080p, 1440p, 2160p, 4k |
|
||||
| `promptOptimizer` | boolean | No | Enable prompt optimization for MiniMax models \(default: true\) |
|
||||
| `generateAudio` | boolean | No | Generate native audio when supported by the selected Fal.ai model |
|
||||
| `pricing` | custom | No | No description |
|
||||
| `metadata` | string | No | No description |
|
||||
| `rateLimit` | string | No | No description |
|
||||
|
||||
#### Output
|
||||
|
||||
|
||||
@@ -149,7 +149,7 @@ Enrich a company by name, domain, LinkedIn ID, or LinkedIn slug with detailed fi
|
||||
| `company_region` | string | State/region |
|
||||
| `company_postal_code` | string | Postal code |
|
||||
| `company_country` | string | Country |
|
||||
| `credits` | json | Remaining API credits |
|
||||
| `credits` | json | Credits deducted for this enrichment \(api_credits: \{ total, email_credits, phone_credits, scrape_credits \}\) |
|
||||
|
||||
### `wiza_start_individual_reveal`
|
||||
|
||||
@@ -227,7 +227,7 @@ Retrieve the status and enriched data for an individual reveal by ID
|
||||
| `company_linkedin` | string | Company LinkedIn URL |
|
||||
| `company_location` | string | Full company location |
|
||||
| `company_description` | string | Company description |
|
||||
| `credits` | json | Remaining credits balance |
|
||||
| `credits` | json | Credits deducted for this reveal \(api_credits: \{ total, email_credits, phone_credits, scrape_credits \}\) |
|
||||
|
||||
### `wiza_get_credits`
|
||||
|
||||
|
||||
@@ -0,0 +1,203 @@
|
||||
---
|
||||
title: ZoomInfo
|
||||
description: Search and enrich B2B company and contact data with ZoomInfo.
|
||||
---
|
||||
|
||||
import { BlockInfoCard } from "@/components/ui/block-info-card"
|
||||
|
||||
<BlockInfoCard
|
||||
type="zoominfo"
|
||||
color="#EA1B15"
|
||||
/>
|
||||
|
||||
{/* MANUAL-CONTENT-START:intro */}
|
||||
[ZoomInfo](https://www.zoominfo.com/) is a B2B go-to-market platform with one of the largest verified databases of company and contact data. It pairs deep firmographic and contact records with buyer intent signals and company news, so your sales, marketing, and operations teams can find, prioritize, and reach the right accounts.
|
||||
|
||||
With ZoomInfo, you can:
|
||||
|
||||
- **Search companies**: Find organizations by name, industry, location, revenue, and employee size
|
||||
- **Search contacts**: Discover people by name, job title, department, and management level
|
||||
- **Enrich companies and contacts**: Resolve detailed firmographics, verified emails, and phone numbers in bulk
|
||||
- **Track buyer intent**: Surface companies showing intent signals on the topics you care about
|
||||
- **Monitor company news**: Pull recent articles by category, source, or date range
|
||||
|
||||
In Sim, the ZoomInfo integration lets your agents power prospecting, enrichment, and account research workflows end-to-end:
|
||||
|
||||
- **Search Companies**: Use `zoominfo_search_companies` to find accounts matching firmographic filters.
|
||||
- **Search Contacts**: Use `zoominfo_search_contacts` to find people by role and company. Search results do not include emails or phone numbers — use Enrich Contacts for engagement data.
|
||||
- **Enrich Companies**: Use `zoominfo_enrich_companies` to resolve up to 25 companies per request from a name, domain, ticker, or ZoomInfo ID.
|
||||
- **Enrich Contacts**: Use `zoominfo_enrich_contacts` to resolve up to 25 contacts per request, returning verified emails, phones, and job details.
|
||||
- **Search Intent**: Use `zoominfo_search_intent` to find companies researching specific topics, with signal scores and recommended contacts.
|
||||
- **Search News**: Use `zoominfo_search_news` to monitor company news by category, source URL, or publish date.
|
||||
|
||||
ZoomInfo authenticates with the OAuth 2.0 client credentials flow: Sim exchanges your client ID and secret for a short-lived bearer token automatically on each call, so you only ever provide your credentials. A common pattern is to search first to identify the records you want, then pass their IDs into the matching enrich tool to pull full detail efficiently.
|
||||
{/* MANUAL-CONTENT-END */}
|
||||
|
||||
|
||||
## Usage Instructions
|
||||
|
||||
Integrates ZoomInfo into the workflow. Search companies and contacts, enrich firmographic and contact data, find intent signals, and pull news — all using the ZoomInfo GTM API.
|
||||
|
||||
|
||||
|
||||
## Tools
|
||||
|
||||
### `zoominfo_search_companies`
|
||||
|
||||
Search the ZoomInfo company database by name, industry, location, and size.
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `clientId` | string | Yes | ZoomInfo OAuth client ID |
|
||||
| `clientSecret` | string | Yes | ZoomInfo OAuth client secret |
|
||||
| `companyName` | string | No | Company name to search for |
|
||||
| `companyWebsite` | string | No | Company website \(comma-separated for multiple\) |
|
||||
| `companyTicker` | string | No | Stock ticker symbols — JSON array, comma-separated list, or single ticker. Sent to the API as an array. |
|
||||
| `industryCodes` | string | No | Industry codes — JSON array or comma-separated list. Sent to the API as a comma-separated string. |
|
||||
| `country` | string | No | Country name |
|
||||
| `state` | string | No | State or province |
|
||||
| `metroRegion` | string | No | US/Canada metro region |
|
||||
| `revenueMin` | number | No | Minimum annual revenue in thousands USD |
|
||||
| `revenueMax` | number | No | Maximum annual revenue in thousands USD |
|
||||
| `employeeRangeMin` | number | No | Minimum employee count |
|
||||
| `employeeRangeMax` | number | No | Maximum employee count |
|
||||
| `excludeDefunctCompanies` | boolean | No | Exclude inactive companies |
|
||||
| `page` | number | No | Page number \(1-based\) |
|
||||
| `rpp` | number | No | Results per page \(1-100, default 25\) |
|
||||
| `sortBy` | string | No | Field to sort by |
|
||||
| `sortOrder` | string | No | Sort order \(asc or desc\) |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `companies` | array | Matching companies |
|
||||
|
||||
### `zoominfo_search_contacts`
|
||||
|
||||
Search ZoomInfo for contacts (people) by name, job title, company, and other filters. Does not return emails or phone numbers — use Enrich Contacts for engagement data.
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `clientId` | string | Yes | ZoomInfo OAuth client ID |
|
||||
| `clientSecret` | string | Yes | ZoomInfo OAuth client secret |
|
||||
| `firstName` | string | No | First name |
|
||||
| `lastName` | string | No | Last name |
|
||||
| `fullName` | string | No | Full name |
|
||||
| `emailAddress` | string | No | Email address |
|
||||
| `jobTitle` | string | No | Job title |
|
||||
| `managementLevel` | string | No | Management level — JSON array or comma-separated list. Sent to the API as a comma-separated string. |
|
||||
| `department` | string | No | Department — JSON array or comma-separated list. Sent to the API as a comma-separated string. |
|
||||
| `companyId` | string | No | ZoomInfo company ID |
|
||||
| `companyName` | string | No | Company name |
|
||||
| `contactAccuracyScoreMin` | number | No | Minimum accuracy score \(70-99\) |
|
||||
| `requiredFields` | string | No | Fields that must exist in results — JSON array or comma-separated list. Sent to the API as a comma-separated string. |
|
||||
| `excludePartialProfiles` | boolean | No | Exclude partial profiles |
|
||||
| `page` | number | No | Page number \(1-based\) |
|
||||
| `rpp` | number | No | Results per page \(1-100, default 25\) |
|
||||
| `sortBy` | string | No | Field to sort by |
|
||||
| `sortOrder` | string | No | Sort order \(asc or desc\) |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `contacts` | array | Matching contacts \(without emails or phone numbers\) |
|
||||
|
||||
### `zoominfo_enrich_companies`
|
||||
|
||||
Enrich up to 25 companies in one request with detailed firmographics, industry, financials, and more.
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `clientId` | string | Yes | ZoomInfo OAuth client ID |
|
||||
| `clientSecret` | string | Yes | ZoomInfo OAuth client secret |
|
||||
| `matchCompanyInput` | string | Yes | JSON array \(1-25 items\) of company matching criteria, e.g. \[\{"companyName":"Acme","companyWebsite":"acme.com"\}\] |
|
||||
| `outputFields` | string | No | JSON array or comma-separated list of fields to return \(e.g. \["id","name","website","revenue","employeeCount"\]\) |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `results` | array | Enrichment results, one per input with match status and attributes |
|
||||
|
||||
### `zoominfo_enrich_contacts`
|
||||
|
||||
Enrich up to 25 contacts in one request with verified emails, phone numbers, job details, and more.
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `clientId` | string | Yes | ZoomInfo OAuth client ID |
|
||||
| `clientSecret` | string | Yes | ZoomInfo OAuth client secret |
|
||||
| `matchPersonInput` | string | Yes | JSON array \(1-25 items\) of contact matching criteria, e.g. \[\{"firstName":"Jane","lastName":"Doe","companyName":"Acme"\}\] |
|
||||
| `outputFields` | string | No | JSON array or comma-separated list of fields to return \(e.g. \["id","firstName","email","phone","jobTitle"\]\) |
|
||||
| `requiredFields` | string | No | JSON array or comma-separated list of fields that must exist in results \(e.g. \["email"\]\) |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `results` | array | Enrichment results, one per input with match status and attributes |
|
||||
|
||||
### `zoominfo_search_intent`
|
||||
|
||||
Search for companies showing intent signals on specific topics.
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `clientId` | string | Yes | ZoomInfo OAuth client ID |
|
||||
| `clientSecret` | string | Yes | ZoomInfo OAuth client secret |
|
||||
| `topics` | string | Yes | Up to 50 intent topics as JSON array or comma-separated list \(e.g. \["CRM Software","Marketing Automation"\]\) |
|
||||
| `signalStartDate` | string | No | Earliest signal date \(YYYY-MM-DD\) |
|
||||
| `signalEndDate` | string | No | Latest signal date \(YYYY-MM-DD\) |
|
||||
| `signalScoreMin` | number | No | Minimum signal score \(60-100\) |
|
||||
| `signalScoreMax` | number | No | Maximum signal score \(60-100\) |
|
||||
| `audienceStrengthMin` | string | No | Minimum audience strength \(A-E, A is largest\) |
|
||||
| `audienceStrengthMax` | string | No | Maximum audience strength \(A-E, A is largest\) |
|
||||
| `findRecommendedContacts` | boolean | No | Include recommended contacts \(default true\) |
|
||||
| `country` | string | No | Country filter |
|
||||
| `state` | string | No | State filter |
|
||||
| `industryCodes` | string | No | Industry codes — JSON array or comma-separated list. Sent to the API as a comma-separated string. |
|
||||
| `page` | number | No | Page number \(1-based\) |
|
||||
| `rpp` | number | No | Results per page \(1-100, default 25\) |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `signals` | array | Intent signals with topic, score, audience strength, and company |
|
||||
|
||||
### `zoominfo_search_news`
|
||||
|
||||
Search ZoomInfo news articles by category, URL, or date range.
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `clientId` | string | Yes | ZoomInfo OAuth client ID |
|
||||
| `clientSecret` | string | Yes | ZoomInfo OAuth client secret |
|
||||
| `categories` | string | No | News categories as JSON array or comma-separated list |
|
||||
| `url` | string | No | News source URLs as JSON array or comma-separated list |
|
||||
| `pageDateMin` | string | No | Earliest publish date \(YYYY-MM-DD\) |
|
||||
| `pageDateMax` | string | No | Latest publish date \(YYYY-MM-DD\) |
|
||||
| `page` | number | No | Page number \(1-based\) |
|
||||
| `rpp` | number | No | Results per page \(1-100, default 25\) |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `articles` | array | News articles matching the filters |
|
||||
|
||||
|
||||
@@ -16,34 +16,42 @@ All triggers below are **polling-based** — they check for new data on a schedu
|
||||
|
||||
## Triggers
|
||||
|
||||
### HubSpot Trigger
|
||||
### HubSpot CRM Trigger
|
||||
|
||||
Triggers when a HubSpot record (contact, company, deal, ticket, or custom object) is created or updated
|
||||
Triggers when HubSpot CRM records (contacts, companies, deals, tickets, custom objects) are created or updated, or when contacts join a list
|
||||
|
||||
#### Configuration
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `triggerCredentials` | string | Yes | Connect a HubSpot account so Sim can poll your CRM on your behalf. |
|
||||
| `objectType` | string | Yes | Which HubSpot CRM object to watch. Pick |
|
||||
| `customObjectTypeId` | string | Yes | HubSpot custom object type ID \(e.g. |
|
||||
| `eventType` | string | Yes | Created fires once per new record. Updated fires whenever the record changes \(and on creation\). |
|
||||
| `properties` | string | No | Comma- or newline-separated list of HubSpot property names to include on each record. Leave empty to use sensible defaults. Sim always includes the timestamp properties Sim needs internally, regardless of this list. |
|
||||
| `filterPropertyName` | string | No | Only emit records where this property equals the value below. Leave both fields empty to emit every change. |
|
||||
| `filterPropertyValue` | string | No | Value the filter property must match \(exact match, case-sensitive\). |
|
||||
| `maxRecordsPerPoll` | string | No | Cap on records emitted per poll \(default 50, max 1000\). Excess rolls over to the next poll. |
|
||||
| `objectType` | string | Yes | What you want to watch. |
|
||||
| `customObjectTypeId` | string | No | HubSpot custom object type ID \(e.g. |
|
||||
| `listId` | string | No | The HubSpot list to watch for new members. |
|
||||
| `eventType` | string | No | Created fires once per new record. Updated fires on any modification. Property Changed fires only when the chosen property changes value. |
|
||||
| `targetPropertyName` | string | No | Fires only when this specific property changes value on a record. |
|
||||
| `properties` | string | No | Properties to include on each record. Leave empty to use sensible defaults. Sim always includes the timestamps it needs internally. |
|
||||
| `pipelineId` | string | No | Restrict to a single pipeline. |
|
||||
| `stageId` | string | No | Restrict to a single stage within the selected pipeline. |
|
||||
| `ownerId` | string | No | Restrict to records owned by a specific HubSpot user. |
|
||||
| `filters` | string | No | JSON array of HubSpot search filters, AND-combined. Each item: \{ |
|
||||
| `maxRecordsPerPoll` | string | No | Soft cap on records emitted per poll \(default 50, max 1000\). Excess rolls over to the next poll. |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `objectType` | string | HubSpot object type that fired the trigger \(contact, company, deal, ticket, or custom object type ID\) |
|
||||
| `eventType` | string | Event type that fired the trigger \(created or updated\) |
|
||||
| `objectId` | string | HubSpot ID of the affected record |
|
||||
| `occurredAt` | string | ISO timestamp of the create or update on the record \(sourced from the relevant HubSpot timestamp property\) |
|
||||
| `properties` | json | HubSpot properties returned for the record \(object of property name to value\) |
|
||||
| `objectType` | string | HubSpot object type \(contact, company, deal, ticket, custom object id, or list_membership\) |
|
||||
| `eventType` | string | Event type \(created, updated, property_changed, or joined\) |
|
||||
| `objectId` | string | HubSpot ID of the affected record \(or contact id for list memberships\) |
|
||||
| `occurredAt` | string | ISO timestamp of when the change happened in HubSpot |
|
||||
| `properties` | json | HubSpot properties on the record as a key-value object \(property internal name → value\). Default keys per object type \(override via "Properties to Fetch"\): Contact → firstname, lastname, email, phone, company, lifecyclestage, hs_lead_status, hubspot_owner_id, createdate, lastmodifieddate. Company → name, domain, industry, lifecyclestage, hubspot_owner_id, createdate, hs_lastmodifieddate. Deal → dealname, amount, dealstage, pipeline, closedate, hubspot_owner_id, createdate, hs_lastmodifieddate. Ticket → subject, content, hs_pipeline, hs_pipeline_stage, hs_ticket_priority, hubspot_owner_id, createdate, hs_lastmodifieddate. Custom and user-requested properties appear keyed by their HubSpot internal name. |
|
||||
| `createdAt` | string | ISO timestamp when the record was created in HubSpot |
|
||||
| `updatedAt` | string | ISO timestamp when the record was last updated in HubSpot |
|
||||
| `archived` | boolean | Whether the record is archived |
|
||||
| `propertyName` | string | Name of the property that changed \(property_changed events only\) |
|
||||
| `propertyValue` | string | New value of the changed property \(property_changed events only\) |
|
||||
| `previousValue` | string | Previous value before the change \(property_changed events only\) |
|
||||
| `listId` | string | HubSpot list ID \(list_membership events only\) |
|
||||
| `timestamp` | string | ISO timestamp when Sim emitted the event |
|
||||
|
||||
|
||||
@@ -208,6 +208,7 @@ import {
|
||||
ZendeskIcon,
|
||||
ZepIcon,
|
||||
ZoomIcon,
|
||||
ZoomInfoIcon,
|
||||
} from '@/components/icons'
|
||||
|
||||
type IconComponent = ComponentType<SVGProps<SVGSVGElement>>
|
||||
@@ -418,4 +419,5 @@ export const blockTypeToIconMap: Record<string, IconComponent> = {
|
||||
zendesk: ZendeskIcon,
|
||||
zep: ZepIcon,
|
||||
zoom: ZoomIcon,
|
||||
zoominfo: ZoomInfoIcon,
|
||||
}
|
||||
|
||||
@@ -214,7 +214,7 @@
|
||||
"name": "AgentPhone",
|
||||
"description": "Provision numbers, send SMS and iMessage, and place voice calls with AgentPhone",
|
||||
"longDescription": "Give your workflow a phone. Provision SMS- and voice-enabled numbers, send messages and tapback reactions, place outbound voice calls, manage conversations and contacts, and track usage — all through a single AgentPhone API key.",
|
||||
"bgColor": "linear-gradient(135deg, #1a1a1a 0%, #0a2a14 100%)",
|
||||
"bgColor": "#000000",
|
||||
"iconName": "AgentPhoneIcon",
|
||||
"docsUrl": "https://docs.sim.ai/tools/agentphone",
|
||||
"operations": [
|
||||
@@ -3730,7 +3730,7 @@
|
||||
"type": "elevenlabs",
|
||||
"slug": "elevenlabs",
|
||||
"name": "ElevenLabs",
|
||||
"description": "Convert TTS using ElevenLabs",
|
||||
"description": "Convert text to speech with ElevenLabs",
|
||||
"longDescription": "Integrate ElevenLabs into the workflow. Can convert text to speech.",
|
||||
"bgColor": "#181C1E",
|
||||
"iconName": "ElevenLabsIcon",
|
||||
@@ -5785,7 +5785,7 @@
|
||||
"slug": "google-slides",
|
||||
"name": "Google Slides",
|
||||
"description": "Read, write, and create presentations",
|
||||
"longDescription": "Integrate Google Slides into the workflow. Can read, write, create presentations, replace text, add slides, add images, get thumbnails, get page details, delete objects, duplicate objects, reorder slides, create tables, create shapes, and insert text.",
|
||||
"longDescription": "Build, edit, and export branded Google Slides presentations end-to-end. Copy a template, replace text and image tokens, embed Sheets charts, style text and shapes with brand fonts and colors, manage tables and layouts, group elements, run atomic batch updates, and export to PDF or PPTX.",
|
||||
"bgColor": "#E0E0E0",
|
||||
"iconName": "GoogleSlidesIcon",
|
||||
"docsUrl": "https://docs.sim.ai/tools/google_slides",
|
||||
@@ -5802,10 +5802,34 @@
|
||||
"name": "Create Presentation",
|
||||
"description": "Create a new Google Slides presentation"
|
||||
},
|
||||
{
|
||||
"name": "Copy Presentation",
|
||||
"description": "Copy a template presentation in Drive to a new file. Use this before merging data so the original template is never modified."
|
||||
},
|
||||
{
|
||||
"name": "Export Presentation",
|
||||
"description": "Export a presentation to PDF, PPTX, ODP, TXT, PNG, JPEG, or SVG via the Drive export endpoint. Stores the exported file as an execution file when execution context is available."
|
||||
},
|
||||
{
|
||||
"name": "Batch Update (Raw)",
|
||||
"description": "Run a raw Slides API batchUpdate with a list of Request objects. Use this when the higher-level tools do not cover an operation, or to bundle multiple operations into a single atomic batch (all-or-nothing)."
|
||||
},
|
||||
{
|
||||
"name": "Replace All Text",
|
||||
"description": "Find and replace all occurrences of text throughout a Google Slides presentation"
|
||||
},
|
||||
{
|
||||
"name": "Replace All Shapes With Image",
|
||||
"description": "Find every shape whose text matches the given token (e.g. {{cover-image}}) and replace it with an image, preserving the shape"
|
||||
},
|
||||
{
|
||||
"name": "Replace Image",
|
||||
"description": "Replace the source of an existing image with a new image URL, preserving the image"
|
||||
},
|
||||
{
|
||||
"name": "Update Image Properties",
|
||||
"description": "Update image properties — brightness, contrast, transparency, crop, outline, link — on an existing image."
|
||||
},
|
||||
{
|
||||
"name": "Add Slide",
|
||||
"description": "Add a new slide to a Google Slides presentation with a specified layout"
|
||||
@@ -5842,12 +5866,140 @@
|
||||
"name": "Create Shape",
|
||||
"description": "Create a shape (rectangle, ellipse, text box, arrow, etc.) on a slide in a Google Slides presentation"
|
||||
},
|
||||
{
|
||||
"name": "Create Line",
|
||||
"description": "Create a line or connector on a slide."
|
||||
},
|
||||
{
|
||||
"name": "Insert Text",
|
||||
"description": "Insert text into a shape or table cell in a Google Slides presentation. Use this to add text to text boxes, shapes, or table cells."
|
||||
},
|
||||
{
|
||||
"name": "Delete Text",
|
||||
"description": "Delete text from a shape or table cell. Use range type ALL to clear all text, or FIXED_RANGE / FROM_START_INDEX to delete a specific span."
|
||||
},
|
||||
{
|
||||
"name": "Update Text Style",
|
||||
"description": "Update the styling of text in a shape or table cell (bold, italic, font family, font size, foreground/background color, link, etc.). Only the fields you set are applied."
|
||||
},
|
||||
{
|
||||
"name": "Update Paragraph Style",
|
||||
"description": "Update paragraph styling — alignment, line spacing, indents, space above/below — for text in a shape or table cell."
|
||||
},
|
||||
{
|
||||
"name": "Create Paragraph Bullets",
|
||||
"description": "Convert paragraphs in a shape or table cell into a bulleted or numbered list using a Google Slides bullet preset."
|
||||
},
|
||||
{
|
||||
"name": "Delete Paragraph Bullets",
|
||||
"description": "Remove bullets/numbering from paragraphs in a shape or table cell."
|
||||
},
|
||||
{
|
||||
"name": "Update Shape Properties",
|
||||
"description": "Update a shape"
|
||||
},
|
||||
{
|
||||
"name": "Update Page Properties",
|
||||
"description": "Update slide/page background — solid color or stretched picture — and other page properties."
|
||||
},
|
||||
{
|
||||
"name": "Update Slide Properties",
|
||||
"description": "Update slide-specific properties such as whether the slide is skipped during presentation."
|
||||
},
|
||||
{
|
||||
"name": "Update Alt Text",
|
||||
"description": "Set the accessibility title and/or description (alt text) of a page element such as an image, shape, or group."
|
||||
},
|
||||
{
|
||||
"name": "Update Element Transform",
|
||||
"description": "Move, resize, scale, or shear a page element. Translate is specified in points; applyMode controls whether the transform is absolute (default) or relative (multiplied with the current transform)."
|
||||
},
|
||||
{
|
||||
"name": "Update Z-Order",
|
||||
"description": "Bring elements to front, send to back, or step them one layer forward/backward."
|
||||
},
|
||||
{
|
||||
"name": "Group Objects",
|
||||
"description": "Group two or more page elements on the same slide into a single object group."
|
||||
},
|
||||
{
|
||||
"name": "Ungroup Objects",
|
||||
"description": "Ungroup one or more object groups, releasing their children back to the slide."
|
||||
},
|
||||
{
|
||||
"name": "Update Line Properties",
|
||||
"description": "Update line appearance — color, weight, dash style, arrows, link."
|
||||
},
|
||||
{
|
||||
"name": "Update Line Category",
|
||||
"description": "Change a connector line"
|
||||
},
|
||||
{
|
||||
"name": "Reroute Line",
|
||||
"description": "Reroute a connector line so it efficiently connects its endpoint shapes — useful after moving the shapes the line connects."
|
||||
},
|
||||
{
|
||||
"name": "Insert Table Rows",
|
||||
"description": "Insert one or more rows into a table, above or below a reference cell."
|
||||
},
|
||||
{
|
||||
"name": "Insert Table Columns",
|
||||
"description": "Insert one or more columns into a table, left or right of a reference cell."
|
||||
},
|
||||
{
|
||||
"name": "Delete Table Row",
|
||||
"description": "Delete the row containing the reference cell from a table."
|
||||
},
|
||||
{
|
||||
"name": "Delete Table Column",
|
||||
"description": "Delete the column containing the reference cell from a table."
|
||||
},
|
||||
{
|
||||
"name": "Merge Table Cells",
|
||||
"description": "Merge a rectangular range of table cells into a single cell. The range starts at (rowIndex, columnIndex) and covers rowSpan × columnSpan cells."
|
||||
},
|
||||
{
|
||||
"name": "Unmerge Table Cells",
|
||||
"description": "Unmerge any merged cells within the given table range."
|
||||
},
|
||||
{
|
||||
"name": "Update Table Cell Properties",
|
||||
"description": "Update background fill and content alignment for a range of table cells."
|
||||
},
|
||||
{
|
||||
"name": "Update Table Border Properties",
|
||||
"description": "Update border color, weight, and dash style for a position (e.g. ALL, INNER, OUTER) in a table range."
|
||||
},
|
||||
{
|
||||
"name": "Update Table Column Properties",
|
||||
"description": "Update column widths and other column-level table properties."
|
||||
},
|
||||
{
|
||||
"name": "Update Table Row Properties",
|
||||
"description": "Update minimum row heights and other row-level table properties."
|
||||
},
|
||||
{
|
||||
"name": "Embed Sheets Chart",
|
||||
"description": "Embed a chart from a Google Sheets spreadsheet onto a slide. LINKED charts can be refreshed; NOT_LINKED_IMAGE inserts a static image of the chart."
|
||||
},
|
||||
{
|
||||
"name": "Refresh Sheets Chart",
|
||||
"description": "Refresh an embedded linked Sheets chart so it reflects the latest spreadsheet data."
|
||||
},
|
||||
{
|
||||
"name": "Replace All Shapes With Sheets Chart",
|
||||
"description": "Find every shape matching a text token (e.g. {{revenue-chart}}) and replace each with the same embedded Sheets chart, preserving the shape"
|
||||
},
|
||||
{
|
||||
"name": "Embed Video",
|
||||
"description": "Embed a YouTube or Google Drive video on a slide."
|
||||
},
|
||||
{
|
||||
"name": "Update Video Properties",
|
||||
"description": "Update video playback options (autoPlay, mute, start/end) and outline."
|
||||
}
|
||||
],
|
||||
"operationCount": 14,
|
||||
"operationCount": 52,
|
||||
"triggers": [],
|
||||
"triggerCount": 0,
|
||||
"authType": "oauth",
|
||||
@@ -6417,7 +6569,7 @@
|
||||
"slug": "hubspot",
|
||||
"name": "HubSpot",
|
||||
"description": "Interact with HubSpot CRM or trigger workflows from HubSpot events",
|
||||
"longDescription": "Integrate HubSpot into your workflow. Manage contacts, companies, deals, tickets, and other CRM objects with powerful automation capabilities. Can be used in trigger mode to start workflows when contacts are created, deleted, or updated.",
|
||||
"longDescription": "Integrate HubSpot into your workflow. Manage contacts, companies, deals, tickets, and other CRM objects with powerful automation capabilities. Can be used in trigger mode to start workflows when records are created, updated, a specific property changes, or a contact joins a list.",
|
||||
"bgColor": "#FF7A59",
|
||||
"iconName": "HubspotIcon",
|
||||
"docsUrl": "https://docs.sim.ai/tools/hubspot",
|
||||
@@ -6542,142 +6694,12 @@
|
||||
"operationCount": 29,
|
||||
"triggers": [
|
||||
{
|
||||
"id": "hubspot_contact_created",
|
||||
"name": "HubSpot Contact Created",
|
||||
"description": "Trigger workflow when a new contact is created in HubSpot"
|
||||
},
|
||||
{
|
||||
"id": "hubspot_contact_deleted",
|
||||
"name": "HubSpot Contact Deleted",
|
||||
"description": "Trigger workflow when a contact is deleted in HubSpot"
|
||||
},
|
||||
{
|
||||
"id": "hubspot_contact_merged",
|
||||
"name": "HubSpot Contact Merged",
|
||||
"description": "Trigger workflow when contacts are merged in HubSpot"
|
||||
},
|
||||
{
|
||||
"id": "hubspot_contact_privacy_deleted",
|
||||
"name": "HubSpot Contact Privacy Deleted",
|
||||
"description": "Trigger workflow when a contact is deleted for privacy compliance (GDPR, CCPA, etc.) in HubSpot"
|
||||
},
|
||||
{
|
||||
"id": "hubspot_contact_property_changed",
|
||||
"name": "HubSpot Contact Property Changed",
|
||||
"description": "Trigger workflow when any property of a contact is updated in HubSpot"
|
||||
},
|
||||
{
|
||||
"id": "hubspot_contact_restored",
|
||||
"name": "HubSpot Contact Restored",
|
||||
"description": "Trigger workflow when a deleted contact is restored in HubSpot"
|
||||
},
|
||||
{
|
||||
"id": "hubspot_company_created",
|
||||
"name": "HubSpot Company Created",
|
||||
"description": "Trigger workflow when a new company is created in HubSpot"
|
||||
},
|
||||
{
|
||||
"id": "hubspot_company_deleted",
|
||||
"name": "HubSpot Company Deleted",
|
||||
"description": "Trigger workflow when a company is deleted in HubSpot"
|
||||
},
|
||||
{
|
||||
"id": "hubspot_company_merged",
|
||||
"name": "HubSpot Company Merged",
|
||||
"description": "Trigger workflow when companies are merged in HubSpot"
|
||||
},
|
||||
{
|
||||
"id": "hubspot_company_property_changed",
|
||||
"name": "HubSpot Company Property Changed",
|
||||
"description": "Trigger workflow when any property of a company is updated in HubSpot"
|
||||
},
|
||||
{
|
||||
"id": "hubspot_company_restored",
|
||||
"name": "HubSpot Company Restored",
|
||||
"description": "Trigger workflow when a deleted company is restored in HubSpot"
|
||||
},
|
||||
{
|
||||
"id": "hubspot_conversation_creation",
|
||||
"name": "HubSpot Conversation Creation",
|
||||
"description": "Trigger workflow when a new conversation is created in HubSpot"
|
||||
},
|
||||
{
|
||||
"id": "hubspot_conversation_deletion",
|
||||
"name": "HubSpot Conversation Deletion",
|
||||
"description": "Trigger workflow when a conversation is deleted in HubSpot"
|
||||
},
|
||||
{
|
||||
"id": "hubspot_conversation_new_message",
|
||||
"name": "HubSpot Conversation New Message",
|
||||
"description": "Trigger workflow when a new message is added to a conversation in HubSpot"
|
||||
},
|
||||
{
|
||||
"id": "hubspot_conversation_privacy_deletion",
|
||||
"name": "HubSpot Conversation Privacy Deletion",
|
||||
"description": "Trigger workflow when a conversation is deleted for privacy compliance (GDPR, CCPA, etc.) in HubSpot"
|
||||
},
|
||||
{
|
||||
"id": "hubspot_conversation_property_changed",
|
||||
"name": "HubSpot Conversation Property Changed",
|
||||
"description": "Trigger workflow when any property of a conversation is updated in HubSpot"
|
||||
},
|
||||
{
|
||||
"id": "hubspot_deal_created",
|
||||
"name": "HubSpot Deal Created",
|
||||
"description": "Trigger workflow when a new deal is created in HubSpot"
|
||||
},
|
||||
{
|
||||
"id": "hubspot_deal_deleted",
|
||||
"name": "HubSpot Deal Deleted",
|
||||
"description": "Trigger workflow when a deal is deleted in HubSpot"
|
||||
},
|
||||
{
|
||||
"id": "hubspot_deal_merged",
|
||||
"name": "HubSpot Deal Merged",
|
||||
"description": "Trigger workflow when deals are merged in HubSpot"
|
||||
},
|
||||
{
|
||||
"id": "hubspot_deal_property_changed",
|
||||
"name": "HubSpot Deal Property Changed",
|
||||
"description": "Trigger workflow when any property of a deal is updated in HubSpot"
|
||||
},
|
||||
{
|
||||
"id": "hubspot_deal_restored",
|
||||
"name": "HubSpot Deal Restored",
|
||||
"description": "Trigger workflow when a deleted deal is restored in HubSpot"
|
||||
},
|
||||
{
|
||||
"id": "hubspot_ticket_created",
|
||||
"name": "HubSpot Ticket Created",
|
||||
"description": "Trigger workflow when a new ticket is created in HubSpot"
|
||||
},
|
||||
{
|
||||
"id": "hubspot_ticket_deleted",
|
||||
"name": "HubSpot Ticket Deleted",
|
||||
"description": "Trigger workflow when a ticket is deleted in HubSpot"
|
||||
},
|
||||
{
|
||||
"id": "hubspot_ticket_merged",
|
||||
"name": "HubSpot Ticket Merged",
|
||||
"description": "Trigger workflow when tickets are merged in HubSpot"
|
||||
},
|
||||
{
|
||||
"id": "hubspot_ticket_property_changed",
|
||||
"name": "HubSpot Ticket Property Changed",
|
||||
"description": "Trigger workflow when any property of a ticket is updated in HubSpot"
|
||||
},
|
||||
{
|
||||
"id": "hubspot_ticket_restored",
|
||||
"name": "HubSpot Ticket Restored",
|
||||
"description": "Trigger workflow when a deleted ticket is restored in HubSpot"
|
||||
},
|
||||
{
|
||||
"id": "hubspot_webhook",
|
||||
"name": "HubSpot Webhook (All Events)",
|
||||
"description": "Trigger workflow on any HubSpot webhook event"
|
||||
"id": "hubspot_poller",
|
||||
"name": "HubSpot CRM Trigger",
|
||||
"description": "Triggers when HubSpot CRM records (contacts, companies, deals, tickets, custom objects) are created or updated, or when contacts join a list"
|
||||
}
|
||||
],
|
||||
"triggerCount": 27,
|
||||
"triggerCount": 1,
|
||||
"authType": "oauth",
|
||||
"category": "tools",
|
||||
"integrationTypes": ["crm", "analytics", "customer-support", "sales"],
|
||||
@@ -15257,5 +15279,48 @@
|
||||
"category": "tools",
|
||||
"integrationTypes": ["communication", "productivity"],
|
||||
"tags": ["meeting", "calendar", "scheduling"]
|
||||
},
|
||||
{
|
||||
"type": "zoominfo",
|
||||
"slug": "zoominfo",
|
||||
"name": "ZoomInfo",
|
||||
"description": "Search and enrich B2B company and contact data with ZoomInfo.",
|
||||
"longDescription": "Integrates ZoomInfo into the workflow. Search companies and contacts, enrich firmographic and contact data, find intent signals, and pull news — all using the ZoomInfo GTM API.",
|
||||
"bgColor": "#EA1B15",
|
||||
"iconName": "ZoomInfoIcon",
|
||||
"docsUrl": "https://docs.sim.ai/tools/zoominfo",
|
||||
"operations": [
|
||||
{
|
||||
"name": "Search Companies",
|
||||
"description": "Search the ZoomInfo company database by name, industry, location, and size."
|
||||
},
|
||||
{
|
||||
"name": "Search Contacts",
|
||||
"description": "Search ZoomInfo for contacts (people) by name, job title, company, and other filters. Does not return emails or phone numbers — use Enrich Contacts for engagement data."
|
||||
},
|
||||
{
|
||||
"name": "Enrich Companies",
|
||||
"description": "Enrich up to 25 companies in one request with detailed firmographics, industry, financials, and more."
|
||||
},
|
||||
{
|
||||
"name": "Enrich Contacts",
|
||||
"description": "Enrich up to 25 contacts in one request with verified emails, phone numbers, job details, and more."
|
||||
},
|
||||
{
|
||||
"name": "Search Intent",
|
||||
"description": "Search for companies showing intent signals on specific topics."
|
||||
},
|
||||
{
|
||||
"name": "Search News",
|
||||
"description": "Search ZoomInfo news articles by category, URL, or date range."
|
||||
}
|
||||
],
|
||||
"operationCount": 6,
|
||||
"triggers": [],
|
||||
"triggerCount": 0,
|
||||
"authType": "none",
|
||||
"category": "tools",
|
||||
"integrationTypes": ["sales"],
|
||||
"tags": ["enrichment", "sales-engagement"]
|
||||
}
|
||||
]
|
||||
|
||||
@@ -0,0 +1,125 @@
|
||||
import { createLogger } from '@sim/logger'
|
||||
import { toError } from '@sim/utils/errors'
|
||||
import { type NextRequest, NextResponse } from 'next/server'
|
||||
import { getValidationErrorMessage, isZodError } from '@/lib/api/server'
|
||||
import { checkInternalAuth } from '@/lib/auth/hybrid'
|
||||
import { secureFetchWithValidation } from '@/lib/core/security/input-validation.server'
|
||||
import { generateRequestId } from '@/lib/core/utils/request'
|
||||
import { withRouteHandler } from '@/lib/core/utils/with-route-handler'
|
||||
import {
|
||||
assertSafeZoomInfoUrl,
|
||||
extractZoomInfoError,
|
||||
fetchZoomInfoAccessToken,
|
||||
ZOOMINFO_API_BASE,
|
||||
ZOOMINFO_OUTBOUND_FETCH_TIMEOUT_MS,
|
||||
type ZoomInfoProxyRequest,
|
||||
ZoomInfoProxyRequestSchema,
|
||||
} from '@/app/api/tools/zoominfo/shared'
|
||||
|
||||
export const dynamic = 'force-dynamic'
|
||||
|
||||
const logger = createLogger('ZoomInfoProxyAPI')
|
||||
|
||||
function buildApiUrl(req: ZoomInfoProxyRequest): string {
|
||||
const subPath = req.path.startsWith('/') ? req.path : `/${req.path}`
|
||||
const url = `${ZOOMINFO_API_BASE}${subPath}`
|
||||
|
||||
if (!req.query || Object.keys(req.query).length === 0) {
|
||||
return url
|
||||
}
|
||||
const search = new URLSearchParams()
|
||||
for (const [key, value] of Object.entries(req.query)) {
|
||||
if (value === undefined || value === null) continue
|
||||
search.append(key, String(value))
|
||||
}
|
||||
const queryString = search.toString()
|
||||
if (!queryString) return url
|
||||
return url.includes('?') ? `${url}&${queryString}` : `${url}?${queryString}`
|
||||
}
|
||||
|
||||
interface Invocation {
|
||||
status: number
|
||||
body: unknown
|
||||
}
|
||||
|
||||
async function callZoomInfo(req: ZoomInfoProxyRequest, accessToken: string): Promise<Invocation> {
|
||||
const url = assertSafeZoomInfoUrl(buildApiUrl(req), 'apiUrl').toString()
|
||||
const hasBody = req.body !== undefined && req.body !== null
|
||||
const headers: Record<string, string> = {
|
||||
Authorization: `Bearer ${accessToken}`,
|
||||
Accept: 'application/json',
|
||||
}
|
||||
if (hasBody) headers['Content-Type'] = 'application/json'
|
||||
|
||||
const response = await secureFetchWithValidation(
|
||||
url,
|
||||
{
|
||||
method: req.method,
|
||||
headers,
|
||||
body: hasBody
|
||||
? typeof req.body === 'string'
|
||||
? req.body
|
||||
: JSON.stringify(req.body)
|
||||
: undefined,
|
||||
timeout: ZOOMINFO_OUTBOUND_FETCH_TIMEOUT_MS,
|
||||
},
|
||||
'apiUrl'
|
||||
)
|
||||
|
||||
const raw = await response.text()
|
||||
let parsed: unknown = null
|
||||
if (raw.length > 0) {
|
||||
try {
|
||||
parsed = JSON.parse(raw)
|
||||
} catch {
|
||||
parsed = raw
|
||||
}
|
||||
}
|
||||
return { status: response.status, body: parsed }
|
||||
}
|
||||
|
||||
export const POST = withRouteHandler(async (request: NextRequest) => {
|
||||
const requestId = generateRequestId()
|
||||
|
||||
try {
|
||||
const authResult = await checkInternalAuth(request, { requireWorkflowId: false })
|
||||
if (!authResult.success) {
|
||||
logger.warn(`[${requestId}] Unauthorized ZoomInfo proxy request: ${authResult.error}`)
|
||||
return NextResponse.json(
|
||||
{ success: false, error: authResult.error || 'Authentication required' },
|
||||
{ status: 401 }
|
||||
)
|
||||
}
|
||||
|
||||
// boundary-raw-json: internal proxy envelope validated by ZoomInfoProxyRequestSchema below; not a public boundary
|
||||
const json = await request.json()
|
||||
const proxyReq = ZoomInfoProxyRequestSchema.parse(json)
|
||||
|
||||
const accessToken = await fetchZoomInfoAccessToken(proxyReq, requestId)
|
||||
const invocation = await callZoomInfo(proxyReq, accessToken)
|
||||
|
||||
if (invocation.status >= 200 && invocation.status < 300) {
|
||||
const data = invocation.status === 204 ? null : invocation.body
|
||||
return NextResponse.json({ success: true, output: { status: invocation.status, data } })
|
||||
}
|
||||
|
||||
const message = extractZoomInfoError(invocation.body, invocation.status)
|
||||
logger.warn(
|
||||
`[${requestId}] ZoomInfo API error (${invocation.status}) ${proxyReq.path}: ${message}`
|
||||
)
|
||||
return NextResponse.json(
|
||||
{ success: false, error: message, status: invocation.status },
|
||||
{ status: invocation.status }
|
||||
)
|
||||
} catch (error) {
|
||||
if (isZodError(error)) {
|
||||
logger.warn(`[${requestId}] Validation error:`, error.issues)
|
||||
return NextResponse.json(
|
||||
{ success: false, error: getValidationErrorMessage(error, 'Validation failed') },
|
||||
{ status: 400 }
|
||||
)
|
||||
}
|
||||
logger.error(`[${requestId}] Unexpected ZoomInfo proxy error:`, error)
|
||||
return NextResponse.json({ success: false, error: toError(error).message }, { status: 500 })
|
||||
}
|
||||
})
|
||||
@@ -0,0 +1,204 @@
|
||||
import { createHash } from 'node:crypto'
|
||||
import { createLogger } from '@sim/logger'
|
||||
import { z } from 'zod'
|
||||
import { secureFetchWithValidation } from '@/lib/core/security/input-validation.server'
|
||||
|
||||
const logger = createLogger('ZoomInfoShared')
|
||||
|
||||
export const ZOOMINFO_API_BASE = 'https://api.zoominfo.com/gtm'
|
||||
export const ZOOMINFO_TOKEN_URL = `${ZOOMINFO_API_BASE}/oauth/v1/token`
|
||||
export const ZOOMINFO_OUTBOUND_FETCH_TIMEOUT_MS = 30_000
|
||||
|
||||
export const ZoomInfoAuthSchema = z.object({
|
||||
clientId: z.string().min(1, 'clientId is required'),
|
||||
clientSecret: z.string().min(1, 'clientSecret is required'),
|
||||
})
|
||||
|
||||
export type ZoomInfoAuth = z.infer<typeof ZoomInfoAuthSchema>
|
||||
|
||||
export const ZoomInfoHttpMethod = z.enum(['GET', 'POST', 'PUT', 'PATCH', 'DELETE'])
|
||||
|
||||
export const ZoomInfoProxyPath = z
|
||||
.string()
|
||||
.min(1, 'path is required')
|
||||
.refine(
|
||||
(p) =>
|
||||
!p.split(/[/\\]/).some((seg) => seg === '..' || seg === '.') &&
|
||||
!p.includes('#') &&
|
||||
!/%(?:2[eEfF]|5[cC]|23)/.test(p),
|
||||
{
|
||||
message:
|
||||
'path must not contain ".." or "." segments, "#", or percent-encoded path/fragment characters',
|
||||
}
|
||||
)
|
||||
|
||||
export const ZoomInfoProxyRequestSchema = ZoomInfoAuthSchema.extend({
|
||||
path: ZoomInfoProxyPath,
|
||||
method: ZoomInfoHttpMethod.default('POST'),
|
||||
query: z.record(z.string(), z.union([z.string(), z.number(), z.boolean()])).optional(),
|
||||
body: z.unknown().optional(),
|
||||
})
|
||||
|
||||
export type ZoomInfoProxyRequest = z.infer<typeof ZoomInfoProxyRequestSchema>
|
||||
|
||||
const FORBIDDEN_HOSTS = new Set([
|
||||
'localhost',
|
||||
'0.0.0.0',
|
||||
'127.0.0.1',
|
||||
'169.254.169.254',
|
||||
'metadata.google.internal',
|
||||
'metadata',
|
||||
'[::1]',
|
||||
'[::]',
|
||||
])
|
||||
|
||||
function isPrivateIPv4(host: string): boolean {
|
||||
const match = host.match(/^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})$/)
|
||||
if (!match) return false
|
||||
const octets = match.slice(1, 5).map(Number) as [number, number, number, number]
|
||||
if (octets.some((o) => o < 0 || o > 255)) return false
|
||||
const [a, b] = octets
|
||||
if (a === 10) return true
|
||||
if (a === 172 && b >= 16 && b <= 31) return true
|
||||
if (a === 192 && b === 168) return true
|
||||
if (a === 127) return true
|
||||
if (a === 169 && b === 254) return true
|
||||
if (a === 0) return true
|
||||
return false
|
||||
}
|
||||
|
||||
export function assertSafeZoomInfoUrl(rawUrl: string, label: string): URL {
|
||||
let parsed: URL
|
||||
try {
|
||||
parsed = new URL(rawUrl)
|
||||
} catch {
|
||||
throw new Error(`${label} must be a valid URL`)
|
||||
}
|
||||
if (parsed.protocol !== 'https:') {
|
||||
throw new Error(`${label} must use https://`)
|
||||
}
|
||||
const host = parsed.hostname.toLowerCase()
|
||||
if (FORBIDDEN_HOSTS.has(host)) {
|
||||
throw new Error(`${label} host is not allowed`)
|
||||
}
|
||||
if (isPrivateIPv4(host)) {
|
||||
throw new Error(`${label} host is not allowed (private/loopback range)`)
|
||||
}
|
||||
if (host !== 'api.zoominfo.com') {
|
||||
throw new Error(`${label} host must be api.zoominfo.com`)
|
||||
}
|
||||
return parsed
|
||||
}
|
||||
|
||||
interface CachedToken {
|
||||
accessToken: string
|
||||
expiresAt: number
|
||||
}
|
||||
|
||||
const TOKEN_CACHE = new Map<string, CachedToken>()
|
||||
const TOKEN_CACHE_MAX_ENTRIES = 500
|
||||
const TOKEN_SAFETY_WINDOW_MS = 60_000
|
||||
|
||||
function tokenCacheKey(auth: ZoomInfoAuth): string {
|
||||
const secretHash = createHash('sha256').update(auth.clientSecret).digest('hex').slice(0, 16)
|
||||
return `${auth.clientId}::${secretHash}`
|
||||
}
|
||||
|
||||
function rememberToken(key: string, token: CachedToken): void {
|
||||
if (TOKEN_CACHE.has(key)) TOKEN_CACHE.delete(key)
|
||||
TOKEN_CACHE.set(key, token)
|
||||
while (TOKEN_CACHE.size > TOKEN_CACHE_MAX_ENTRIES) {
|
||||
const oldestKey = TOKEN_CACHE.keys().next().value
|
||||
if (oldestKey === undefined) break
|
||||
TOKEN_CACHE.delete(oldestKey)
|
||||
}
|
||||
}
|
||||
|
||||
export async function fetchZoomInfoAccessToken(
|
||||
auth: ZoomInfoAuth,
|
||||
requestId: string
|
||||
): Promise<string> {
|
||||
const cacheKey = tokenCacheKey(auth)
|
||||
const cached = TOKEN_CACHE.get(cacheKey)
|
||||
if (cached && cached.expiresAt - TOKEN_SAFETY_WINDOW_MS > Date.now()) {
|
||||
return cached.accessToken
|
||||
}
|
||||
|
||||
const tokenUrl = assertSafeZoomInfoUrl(ZOOMINFO_TOKEN_URL, 'tokenUrl').toString()
|
||||
const basic = Buffer.from(`${auth.clientId}:${auth.clientSecret}`).toString('base64')
|
||||
|
||||
const params = new URLSearchParams()
|
||||
params.set('grant_type', 'client_credentials')
|
||||
|
||||
const response = await secureFetchWithValidation(
|
||||
tokenUrl,
|
||||
{
|
||||
method: 'POST',
|
||||
headers: {
|
||||
Authorization: `Basic ${basic}`,
|
||||
'Content-Type': 'application/x-www-form-urlencoded',
|
||||
Accept: 'application/json',
|
||||
},
|
||||
body: params.toString(),
|
||||
timeout: ZOOMINFO_OUTBOUND_FETCH_TIMEOUT_MS,
|
||||
},
|
||||
'tokenUrl'
|
||||
)
|
||||
|
||||
if (!response.ok) {
|
||||
const text = await response.text().catch(() => '')
|
||||
logger.warn(`[${requestId}] ZoomInfo token fetch failed (${response.status}): ${text}`)
|
||||
throw new Error(`ZoomInfo token request failed: HTTP ${response.status}`)
|
||||
}
|
||||
|
||||
const data = (await response.json()) as {
|
||||
access_token?: string
|
||||
expires_in?: number
|
||||
}
|
||||
|
||||
if (!data.access_token) {
|
||||
throw new Error('ZoomInfo token response missing access_token')
|
||||
}
|
||||
|
||||
const expiresInMs = (data.expires_in ?? 3300) * 1000
|
||||
rememberToken(cacheKey, {
|
||||
accessToken: data.access_token,
|
||||
expiresAt: Date.now() + expiresInMs,
|
||||
})
|
||||
return data.access_token
|
||||
}
|
||||
|
||||
export function extractZoomInfoError(body: unknown, status: number): string {
|
||||
if (body && typeof body === 'object') {
|
||||
const obj = body as Record<string, unknown>
|
||||
if (obj.error && typeof obj.error === 'object') {
|
||||
const eo = obj.error as Record<string, unknown>
|
||||
const message = typeof eo.message === 'string' ? eo.message : ''
|
||||
const code = typeof eo.code === 'string' ? eo.code : ''
|
||||
if (message) return code ? `[${code}] ${message}` : message
|
||||
}
|
||||
if (typeof obj.error === 'string' && obj.error.length > 0) {
|
||||
const desc = typeof obj.error_description === 'string' ? `: ${obj.error_description}` : ''
|
||||
return `${obj.error}${desc}`
|
||||
}
|
||||
if (typeof obj.message === 'string' && obj.message.length > 0) {
|
||||
return obj.message
|
||||
}
|
||||
if (Array.isArray(obj.errors) && obj.errors.length > 0) {
|
||||
return obj.errors
|
||||
.map((e) => {
|
||||
if (e && typeof e === 'object') {
|
||||
const eo = e as Record<string, unknown>
|
||||
const title = typeof eo.title === 'string' ? eo.title : ''
|
||||
const detail = typeof eo.detail === 'string' ? `: ${eo.detail}` : ''
|
||||
return `${title}${detail}`.trim()
|
||||
}
|
||||
return String(e)
|
||||
})
|
||||
.filter(Boolean)
|
||||
.join('; ')
|
||||
}
|
||||
}
|
||||
if (typeof body === 'string' && body.length > 0) return body
|
||||
return `ZoomInfo request failed with HTTP ${status}`
|
||||
}
|
||||
@@ -224,11 +224,10 @@ export const ApolloBlock: BlockConfig<ApolloResponse> = {
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
id: 'organizations',
|
||||
title: 'Organizations (JSON Array)',
|
||||
id: 'domains',
|
||||
title: 'Domains (JSON Array)',
|
||||
type: 'code',
|
||||
placeholder:
|
||||
'[{"name": "Company A", "domain": "companya.com"}, {"name": "Company B", "domain": "companyb.com"}]',
|
||||
placeholder: '["apollo.io", "stripe.com"]',
|
||||
condition: { field: 'operation', value: 'organization_bulk_enrich' },
|
||||
required: true,
|
||||
},
|
||||
@@ -589,6 +588,14 @@ export const ApolloBlock: BlockConfig<ApolloResponse> = {
|
||||
condition: { field: 'operation', value: 'account_bulk_update' },
|
||||
mode: 'advanced',
|
||||
},
|
||||
{
|
||||
id: 'account_bulk_update_account_stage_id',
|
||||
title: 'Uniform Account Stage ID (used with Account IDs)',
|
||||
type: 'short-input',
|
||||
placeholder: 'Apollo account stage ID',
|
||||
condition: { field: 'operation', value: 'account_bulk_update' },
|
||||
mode: 'advanced',
|
||||
},
|
||||
{
|
||||
id: 'account_attributes',
|
||||
title: 'Account Attributes (JSON Array of Objects)',
|
||||
@@ -1027,6 +1034,7 @@ Return ONLY the timestamp string in ISO 8601 format - no explanations, no quotes
|
||||
'account_stage_ids',
|
||||
'account_label_ids',
|
||||
'people',
|
||||
'domains',
|
||||
'organizations',
|
||||
'contacts',
|
||||
'accounts',
|
||||
@@ -1075,6 +1083,29 @@ Return ONLY the timestamp string in ISO 8601 format - no explanations, no quotes
|
||||
}
|
||||
}
|
||||
|
||||
if (params.operation === 'organization_bulk_enrich') {
|
||||
// Back-compat: workflows saved before the `organizations` → `domains` rename stored an
|
||||
// array of { name, domain? } objects (or plain strings) under `organizations`. Derive
|
||||
// `domains` from it so those workflows keep running without manual migration.
|
||||
if (parsedParams.domains === undefined && parsedParams.organizations !== undefined) {
|
||||
const legacy = parsedParams.organizations
|
||||
if (Array.isArray(legacy)) {
|
||||
const derived = legacy
|
||||
.map((item) => {
|
||||
if (typeof item === 'string') return item
|
||||
if (item && typeof item === 'object' && 'domain' in item) {
|
||||
const domain = (item as Record<string, unknown>).domain
|
||||
return typeof domain === 'string' ? domain : undefined
|
||||
}
|
||||
return undefined
|
||||
})
|
||||
.filter((domain): domain is string => typeof domain === 'string' && domain !== '')
|
||||
if (derived.length > 0) parsedParams.domains = derived
|
||||
}
|
||||
}
|
||||
parsedParams.organizations = undefined
|
||||
}
|
||||
|
||||
if (params.operation === 'contact_bulk_update') {
|
||||
const { ids, attributes } = splitBulkUpdateInput(parsedParams.contacts)
|
||||
if (attributes) {
|
||||
@@ -1103,8 +1134,12 @@ Return ONLY the timestamp string in ISO 8601 format - no explanations, no quotes
|
||||
if (rest.account_bulk_update_owner_id) {
|
||||
parsedParams.owner_id = rest.account_bulk_update_owner_id
|
||||
}
|
||||
if (rest.account_bulk_update_account_stage_id) {
|
||||
parsedParams.account_stage_id = rest.account_bulk_update_account_stage_id
|
||||
}
|
||||
parsedParams.account_bulk_update_name = undefined
|
||||
parsedParams.account_bulk_update_owner_id = undefined
|
||||
parsedParams.account_bulk_update_account_stage_id = undefined
|
||||
}
|
||||
|
||||
if (params.operation === 'contact_create') {
|
||||
@@ -1168,7 +1203,172 @@ Return ONLY the timestamp string in ISO 8601 format - no explanations, no quotes
|
||||
operation: { type: 'string', description: 'Apollo operation to perform' },
|
||||
},
|
||||
outputs: {
|
||||
success: { type: 'boolean', description: 'Whether the operation was successful' },
|
||||
output: { type: 'json', description: 'Output data from the Apollo operation' },
|
||||
people: {
|
||||
type: 'json',
|
||||
description:
|
||||
'Array of people (people_search): [{id, first_name, last_name, name, title, email, organization_name, linkedin_url, phone_numbers}]',
|
||||
},
|
||||
person: {
|
||||
type: 'json',
|
||||
description:
|
||||
'Enriched person (people_enrich): {id, first_name, last_name, name, title, email, organization_name, linkedin_url, phone_numbers}',
|
||||
},
|
||||
matches: {
|
||||
type: 'json',
|
||||
description: 'Array of enriched people (people_bulk_enrich), null entries indicate no match',
|
||||
},
|
||||
organizations: {
|
||||
type: 'json',
|
||||
description:
|
||||
'Array of organizations (organization_search, organization_bulk_enrich): [{id, name, website_url, linkedin_url, industry, phone, employees, founded_year}]',
|
||||
},
|
||||
organization: {
|
||||
type: 'json',
|
||||
description:
|
||||
'Enriched organization (organization_enrich): {id, name, website_url, linkedin_url, industry, phone, employees, founded_year}',
|
||||
},
|
||||
contact: {
|
||||
type: 'json',
|
||||
description:
|
||||
'Contact (contact_create, contact_update): {id, first_name, last_name, email, title, account_id, owner_id, created_at}',
|
||||
},
|
||||
contacts: {
|
||||
type: 'json',
|
||||
description: 'Array of contacts (contact_search)',
|
||||
},
|
||||
created_contacts: {
|
||||
type: 'json',
|
||||
description: 'Newly created contacts (contact_bulk_create)',
|
||||
},
|
||||
existing_contacts: {
|
||||
type: 'json',
|
||||
description: 'Existing contacts (contact_bulk_create with dedupe)',
|
||||
},
|
||||
account: {
|
||||
type: 'json',
|
||||
description:
|
||||
'Account (account_create, account_update): {id, name, domain, website_url, phone, owner_id, account_stage_id, created_at}',
|
||||
},
|
||||
accounts: {
|
||||
type: 'json',
|
||||
description: 'Array of accounts (account_search)',
|
||||
},
|
||||
created_accounts: {
|
||||
type: 'json',
|
||||
description: 'Newly created accounts (account_bulk_create)',
|
||||
},
|
||||
existing_accounts: {
|
||||
type: 'json',
|
||||
description: 'Existing accounts (account_bulk_create with dedupe)',
|
||||
},
|
||||
failed_accounts: {
|
||||
type: 'json',
|
||||
description: 'Accounts that failed (account_bulk_create)',
|
||||
},
|
||||
account_ids: {
|
||||
type: 'json',
|
||||
description: 'IDs of updated accounts (account_bulk_update)',
|
||||
},
|
||||
entity_progress_job: {
|
||||
type: 'json',
|
||||
description: 'Async job descriptor (contact_bulk_update, account_bulk_update async path)',
|
||||
},
|
||||
opportunity: {
|
||||
type: 'json',
|
||||
description:
|
||||
'Opportunity (opportunity_create, opportunity_update, opportunity_get): {id, name, account_id, amount, opportunity_stage_id, owner_id, closed_date, is_closed, is_won, currency, created_at}',
|
||||
},
|
||||
opportunities: {
|
||||
type: 'json',
|
||||
description: 'Array of opportunities (opportunity_search)',
|
||||
},
|
||||
sequences: {
|
||||
type: 'json',
|
||||
description:
|
||||
'Array of sequences (sequence_search): [{id, name, active, num_steps, num_contacts, created_at}]',
|
||||
},
|
||||
added: {
|
||||
type: 'json',
|
||||
description:
|
||||
'Contacts added to sequence (sequence_add): [{id, first_name, last_name, email, status}]',
|
||||
},
|
||||
skipped: {
|
||||
type: 'json',
|
||||
description: 'Contacts skipped by sequence add (sequence_add)',
|
||||
},
|
||||
skipped_contact_ids: {
|
||||
type: 'json',
|
||||
description: 'Skipped contact IDs (sequence_add): array of IDs or {id: reason} map',
|
||||
},
|
||||
emailer_campaign: {
|
||||
type: 'json',
|
||||
description: 'Emailer campaign details (sequence_add): {id, name}',
|
||||
},
|
||||
sequence_id: {
|
||||
type: 'string',
|
||||
description: 'Sequence ID contacts were added to (sequence_add)',
|
||||
},
|
||||
tasks: {
|
||||
type: 'json',
|
||||
description:
|
||||
'Array of tasks (task_create, task_search): [{id, user_id, contact_id, type, priority, status, due_at, note, created_at}]',
|
||||
},
|
||||
email_accounts: {
|
||||
type: 'json',
|
||||
description:
|
||||
'Linked email accounts (email_accounts): [{id, email, type, active, default, linked_at}]',
|
||||
},
|
||||
pagination: {
|
||||
type: 'json',
|
||||
description: 'Pagination info (contact_search, account_search, task_search)',
|
||||
},
|
||||
page: { type: 'number', description: 'Current page (search operations)' },
|
||||
per_page: { type: 'number', description: 'Results per page (search operations)' },
|
||||
total_entries: {
|
||||
type: 'number',
|
||||
description: 'Total entries matching search (search operations)',
|
||||
},
|
||||
total_added: { type: 'number', description: 'Contacts added (sequence_add)' },
|
||||
total_skipped: { type: 'number', description: 'Contacts skipped (sequence_add)' },
|
||||
total_submitted: {
|
||||
type: 'number',
|
||||
description: 'Total submitted (contact_bulk_create, account_bulk_create)',
|
||||
},
|
||||
created: {
|
||||
type: 'boolean',
|
||||
description: 'Created flag for single-item create operations',
|
||||
},
|
||||
updated: { type: 'boolean', description: 'Updated flag for single-item update operations' },
|
||||
found: { type: 'boolean', description: 'Found flag (opportunity_get)' },
|
||||
enriched: {
|
||||
type: 'boolean',
|
||||
description: 'Enriched flag (people_enrich, organization_enrich)',
|
||||
},
|
||||
message: { type: 'string', description: 'Message (bulk_update operations)' },
|
||||
job_id: { type: 'string', description: 'Async job ID (bulk_update operations)' },
|
||||
total: {
|
||||
type: 'number',
|
||||
description: 'Total count (organization_bulk_enrich requested domains; email_accounts count)',
|
||||
},
|
||||
total_requested_enrichments: {
|
||||
type: 'number',
|
||||
description: 'Total requested enrichments (people_bulk_enrich)',
|
||||
},
|
||||
unique_enriched_records: {
|
||||
type: 'number',
|
||||
description: 'Unique enriched records (people_bulk_enrich)',
|
||||
},
|
||||
unique_domains: {
|
||||
type: 'number',
|
||||
description: 'Unique domains processed (organization_bulk_enrich)',
|
||||
},
|
||||
missing_records: {
|
||||
type: 'number',
|
||||
description: 'Missing records (people_bulk_enrich, organization_bulk_enrich)',
|
||||
},
|
||||
credits_consumed: {
|
||||
type: 'number',
|
||||
description: 'Credits consumed (people_bulk_enrich)',
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
@@ -54,6 +54,15 @@ export const WizaBlock: BlockConfig<WizaResponse> = {
|
||||
placeholder: '[{"v":"CEO","s":"i"},{"v":"Founder","s":"i"}]',
|
||||
condition: { field: 'operation', value: 'prospect_search' },
|
||||
mode: 'advanced',
|
||||
wandConfig: {
|
||||
enabled: true,
|
||||
prompt: `Generate a Wiza job_title filter: a JSON array of include/exclude objects.
|
||||
Each object is {"v": "<job title>", "s": "i" | "e"} where "i" includes and "e" excludes the title. Use double quotes around a phrase for an exact match.
|
||||
Example: [{"v":"CEO","s":"i"},{"v":"intern","s":"e"}]
|
||||
Return ONLY the JSON array - no explanations, no extra text.`,
|
||||
placeholder: 'Describe the job titles to include/exclude...',
|
||||
generationType: 'json-object',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'job_title_level',
|
||||
@@ -102,6 +111,15 @@ export const WizaBlock: BlockConfig<WizaResponse> = {
|
||||
placeholder: '[{"v":{"country":"united states"},"b":"city","s":"i"}]',
|
||||
condition: { field: 'operation', value: 'prospect_search' },
|
||||
mode: 'advanced',
|
||||
wandConfig: {
|
||||
enabled: true,
|
||||
prompt: `Generate a Wiza person location filter: a JSON array of location objects.
|
||||
Each object is {"v": {"country": "...", "state": "...", "city": "..."}, "b": "country" | "state" | "city", "s": "i" | "e"} where "b" is the level to match, "s" includes ("i") or excludes ("e").
|
||||
Example: [{"v":{"country":"united states","state":"california"},"b":"state","s":"i"}]
|
||||
Return ONLY the JSON array - no explanations, no extra text.`,
|
||||
placeholder: 'Describe the person locations to include/exclude...',
|
||||
generationType: 'json-object',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'skill',
|
||||
@@ -142,6 +160,15 @@ export const WizaBlock: BlockConfig<WizaResponse> = {
|
||||
placeholder: '[{"v":"wiza","s":"i"}]',
|
||||
condition: { field: 'operation', value: 'prospect_search' },
|
||||
mode: 'advanced',
|
||||
wandConfig: {
|
||||
enabled: true,
|
||||
prompt: `Generate a Wiza current-company filter: a JSON array of include/exclude objects.
|
||||
Each object is {"v": "<company name>", "s": "i" | "e"} where "i" includes and "e" excludes the company.
|
||||
Example: [{"v":"wiza","s":"i"},{"v":"acme","s":"e"}]
|
||||
Return ONLY the JSON array - no explanations, no extra text.`,
|
||||
placeholder: 'Describe the current companies to include/exclude...',
|
||||
generationType: 'json-object',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'past_company',
|
||||
@@ -150,6 +177,15 @@ export const WizaBlock: BlockConfig<WizaResponse> = {
|
||||
placeholder: '[{"v":"google","s":"i"}]',
|
||||
condition: { field: 'operation', value: 'prospect_search' },
|
||||
mode: 'advanced',
|
||||
wandConfig: {
|
||||
enabled: true,
|
||||
prompt: `Generate a Wiza past-company filter: a JSON array of include/exclude objects.
|
||||
Each object is {"v": "<company name>", "s": "i" | "e"} where "i" includes and "e" excludes the company.
|
||||
Example: [{"v":"google","s":"i"}]
|
||||
Return ONLY the JSON array - no explanations, no extra text.`,
|
||||
placeholder: 'Describe the past companies to include/exclude...',
|
||||
generationType: 'json-object',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'company_location',
|
||||
@@ -158,6 +194,15 @@ export const WizaBlock: BlockConfig<WizaResponse> = {
|
||||
placeholder: '[{"v":{"country":"canada"},"b":"country","s":"i"}]',
|
||||
condition: { field: 'operation', value: 'prospect_search' },
|
||||
mode: 'advanced',
|
||||
wandConfig: {
|
||||
enabled: true,
|
||||
prompt: `Generate a Wiza company HQ location filter: a JSON array of location objects.
|
||||
Each object is {"v": {"country": "...", "state": "...", "city": "..."}, "b": "country" | "state" | "city", "s": "i" | "e"} where "b" is the level to match, "s" includes ("i") or excludes ("e").
|
||||
Example: [{"v":{"country":"canada","state":"ontario"},"b":"state","s":"i"}]
|
||||
Return ONLY the JSON array - no explanations, no extra text.`,
|
||||
placeholder: 'Describe the company HQ locations to include/exclude...',
|
||||
generationType: 'json-object',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'company_industry',
|
||||
@@ -166,6 +211,15 @@ export const WizaBlock: BlockConfig<WizaResponse> = {
|
||||
placeholder: '[{"v":"computer software","s":"i"}]',
|
||||
condition: { field: 'operation', value: 'prospect_search' },
|
||||
mode: 'advanced',
|
||||
wandConfig: {
|
||||
enabled: true,
|
||||
prompt: `Generate a Wiza company industry filter: a JSON array of include/exclude objects.
|
||||
Each object is {"v": "<industry>", "s": "i" | "e"} where "i" includes and "e" excludes the industry.
|
||||
Example: [{"v":"computer software","s":"i"},{"v":"retail","s":"e"}]
|
||||
Return ONLY the JSON array - no explanations, no extra text.`,
|
||||
placeholder: 'Describe the company industries to include/exclude...',
|
||||
generationType: 'json-object',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'company_size',
|
||||
@@ -190,6 +244,15 @@ export const WizaBlock: BlockConfig<WizaResponse> = {
|
||||
placeholder: '{"job_title":[{"v":"CEO","s":"i"}], "company_size":["11-50"]}',
|
||||
condition: { field: 'operation', value: 'prospect_search' },
|
||||
mode: 'advanced',
|
||||
wandConfig: {
|
||||
enabled: true,
|
||||
prompt: `Generate a Wiza prospect-search filters object as JSON.
|
||||
Available keys: first_name, last_name (string arrays); job_title, job_company, past_company, company_industry (arrays of {"v":"...","s":"i"|"e"}); location, company_location (arrays of {"v":{country,state,city},"b":"country"|"state"|"city","s":"i"|"e"}); job_title_level, job_role, job_sub_role, skill, school, major, linkedin_slug, company_size, company_type (string arrays).
|
||||
Example: {"job_title":[{"v":"CEO","s":"i"}],"company_size":["11-50","51-200"],"company_location":[{"v":{"country":"united states"},"b":"country","s":"i"}]}
|
||||
Return ONLY the JSON object - no explanations, no extra text.`,
|
||||
placeholder: 'Describe the prospects to search for...',
|
||||
generationType: 'json-object',
|
||||
},
|
||||
},
|
||||
|
||||
// Company Enrichment
|
||||
@@ -235,7 +298,7 @@ export const WizaBlock: BlockConfig<WizaResponse> = {
|
||||
{ label: 'Phone', id: 'phone' },
|
||||
{ label: 'Full', id: 'full' },
|
||||
],
|
||||
value: () => 'full',
|
||||
value: () => 'partial',
|
||||
condition: { field: 'operation', value: 'start_individual_reveal' },
|
||||
required: { field: 'operation', value: 'start_individual_reveal' },
|
||||
},
|
||||
@@ -421,7 +484,159 @@ export const WizaBlock: BlockConfig<WizaResponse> = {
|
||||
},
|
||||
|
||||
outputs: {
|
||||
success: { type: 'boolean', description: 'Whether the operation was successful' },
|
||||
output: { type: 'json', description: 'Output data from the Wiza operation' },
|
||||
total: {
|
||||
type: 'number',
|
||||
description: 'Total prospects matching filters (prospect_search)',
|
||||
},
|
||||
profiles: {
|
||||
type: 'json',
|
||||
description:
|
||||
'Sample prospect profiles (prospect_search): [{full_name, linkedin_url, industry, job_title, job_title_role, job_title_sub_role, job_company_name, job_company_website, location_name}]',
|
||||
},
|
||||
id: {
|
||||
type: 'number',
|
||||
description: 'Reveal ID (start_individual_reveal, get_individual_reveal)',
|
||||
},
|
||||
status: {
|
||||
type: 'string',
|
||||
description:
|
||||
'Reveal status (start_individual_reveal, get_individual_reveal): queued | resolving | finished | failed',
|
||||
},
|
||||
is_complete: {
|
||||
type: 'boolean',
|
||||
description:
|
||||
'Whether the reveal has completed (start_individual_reveal, get_individual_reveal)',
|
||||
},
|
||||
name: { type: 'string', description: 'Full name (get_individual_reveal)' },
|
||||
company: { type: 'string', description: 'Company name (get_individual_reveal)' },
|
||||
enrichment_level: {
|
||||
type: 'string',
|
||||
description: 'Enrichment level used (get_individual_reveal)',
|
||||
},
|
||||
linkedin_profile_url: { type: 'string', description: 'LinkedIn URL (get_individual_reveal)' },
|
||||
title: { type: 'string', description: 'Job title (get_individual_reveal)' },
|
||||
location: { type: 'string', description: 'Location (get_individual_reveal)' },
|
||||
email: { type: 'string', description: 'Primary email (get_individual_reveal)' },
|
||||
email_type: { type: 'string', description: 'Primary email type (get_individual_reveal)' },
|
||||
email_status: {
|
||||
type: 'string',
|
||||
description: 'Primary email status: valid | risky | unfound (get_individual_reveal)',
|
||||
},
|
||||
emails: {
|
||||
type: 'json',
|
||||
description: 'All emails found (get_individual_reveal): [{email, email_type, email_status}]',
|
||||
},
|
||||
mobile_phone: { type: 'string', description: 'Mobile phone (get_individual_reveal)' },
|
||||
phone_number: { type: 'string', description: 'Direct/office phone (get_individual_reveal)' },
|
||||
phone_status: {
|
||||
type: 'string',
|
||||
description: 'Phone status: found | unfound (get_individual_reveal)',
|
||||
},
|
||||
phones: {
|
||||
type: 'json',
|
||||
description: 'All phones found (get_individual_reveal): [{number, pretty_number, type}]',
|
||||
},
|
||||
company_name: {
|
||||
type: 'string',
|
||||
description: 'Company name (company_enrichment)',
|
||||
},
|
||||
company_domain: {
|
||||
type: 'string',
|
||||
description: 'Company domain (company_enrichment, get_individual_reveal)',
|
||||
},
|
||||
domain: { type: 'string', description: 'Domain (company_enrichment)' },
|
||||
company_industry: {
|
||||
type: 'string',
|
||||
description: 'Industry (company_enrichment, get_individual_reveal)',
|
||||
},
|
||||
company_size: {
|
||||
type: 'number',
|
||||
description: 'Employee count (company_enrichment, get_individual_reveal)',
|
||||
},
|
||||
company_size_range: {
|
||||
type: 'string',
|
||||
description: 'Headcount range (company_enrichment, get_individual_reveal)',
|
||||
},
|
||||
company_founded: {
|
||||
type: 'number',
|
||||
description: 'Year founded (company_enrichment, get_individual_reveal)',
|
||||
},
|
||||
company_revenue_range: {
|
||||
type: 'string',
|
||||
description: 'Revenue range (company_enrichment)',
|
||||
},
|
||||
company_revenue: { type: 'string', description: 'Revenue (get_individual_reveal)' },
|
||||
company_funding: {
|
||||
type: 'string',
|
||||
description: 'Total funding (company_enrichment, get_individual_reveal)',
|
||||
},
|
||||
company_type: {
|
||||
type: 'string',
|
||||
description: 'Company type (company_enrichment, get_individual_reveal)',
|
||||
},
|
||||
company_description: {
|
||||
type: 'string',
|
||||
description: 'Company description (company_enrichment, get_individual_reveal)',
|
||||
},
|
||||
company_ticker: { type: 'string', description: 'Stock ticker (company_enrichment)' },
|
||||
company_last_funding_round: {
|
||||
type: 'string',
|
||||
description: 'Last funding round (company_enrichment)',
|
||||
},
|
||||
company_last_funding_amount: {
|
||||
type: 'string',
|
||||
description: 'Last funding amount (company_enrichment)',
|
||||
},
|
||||
company_last_funding_at: {
|
||||
type: 'string',
|
||||
description: 'Last funding date (company_enrichment)',
|
||||
},
|
||||
company_location: {
|
||||
type: 'string',
|
||||
description: 'Full location string (company_enrichment, get_individual_reveal)',
|
||||
},
|
||||
company_twitter: { type: 'string', description: 'Twitter URL (company_enrichment)' },
|
||||
company_facebook: { type: 'string', description: 'Facebook URL (company_enrichment)' },
|
||||
company_linkedin: {
|
||||
type: 'string',
|
||||
description: 'LinkedIn URL (company_enrichment, get_individual_reveal)',
|
||||
},
|
||||
company_linkedin_id: { type: 'string', description: 'LinkedIn ID (company_enrichment)' },
|
||||
company_street: {
|
||||
type: 'string',
|
||||
description: 'Street address (company_enrichment, get_individual_reveal)',
|
||||
},
|
||||
company_locality: {
|
||||
type: 'string',
|
||||
description: 'City (company_enrichment, get_individual_reveal)',
|
||||
},
|
||||
company_region: {
|
||||
type: 'string',
|
||||
description: 'State/region (company_enrichment, get_individual_reveal)',
|
||||
},
|
||||
company_postal_code: {
|
||||
type: 'string',
|
||||
description: 'Postal code (company_enrichment, get_individual_reveal)',
|
||||
},
|
||||
company_country: {
|
||||
type: 'string',
|
||||
description: 'Country (company_enrichment, get_individual_reveal)',
|
||||
},
|
||||
company_subindustry: { type: 'string', description: 'Subindustry (get_individual_reveal)' },
|
||||
credits: {
|
||||
type: 'json',
|
||||
description:
|
||||
'Credits deducted — company_enrichment: { api_credits: { total, company_credits } }; get_individual_reveal: { api_credits: { total, email_credits, phone_credits, scrape_credits } }',
|
||||
},
|
||||
email_credits: {
|
||||
type: 'json',
|
||||
description: 'Remaining email credits — number or "unlimited" (get_credits)',
|
||||
},
|
||||
phone_credits: {
|
||||
type: 'json',
|
||||
description: 'Remaining phone credits — number or "unlimited" (get_credits)',
|
||||
},
|
||||
export_credits: { type: 'number', description: 'Remaining export credits (get_credits)' },
|
||||
api_credits: { type: 'number', description: 'Remaining API credits (get_credits)' },
|
||||
},
|
||||
}
|
||||
|
||||
@@ -0,0 +1,554 @@
|
||||
import { ZoomInfoIcon } from '@/components/icons'
|
||||
import type { BlockConfig } from '@/blocks/types'
|
||||
import { AuthMode, IntegrationType } from '@/blocks/types'
|
||||
import type { ZoomInfoResponse } from '@/tools/zoominfo/types'
|
||||
|
||||
export const ZoomInfoBlock: BlockConfig<ZoomInfoResponse> = {
|
||||
type: 'zoominfo',
|
||||
name: 'ZoomInfo',
|
||||
description: 'Search and enrich B2B company and contact data with ZoomInfo.',
|
||||
authMode: AuthMode.ApiKey,
|
||||
longDescription:
|
||||
'Integrates ZoomInfo into the workflow. Search companies and contacts, enrich firmographic and contact data, find intent signals, and pull news — all using the ZoomInfo GTM API.',
|
||||
docsLink: 'https://docs.sim.ai/tools/zoominfo',
|
||||
category: 'tools',
|
||||
integrationType: IntegrationType.Sales,
|
||||
tags: ['enrichment', 'sales-engagement'],
|
||||
bgColor: '#EA1B15',
|
||||
icon: ZoomInfoIcon,
|
||||
subBlocks: [
|
||||
{
|
||||
id: 'operation',
|
||||
title: 'Operation',
|
||||
type: 'dropdown',
|
||||
options: [
|
||||
{ label: 'Search Companies', id: 'search_companies' },
|
||||
{ label: 'Search Contacts', id: 'search_contacts' },
|
||||
{ label: 'Enrich Companies', id: 'enrich_companies' },
|
||||
{ label: 'Enrich Contacts', id: 'enrich_contacts' },
|
||||
{ label: 'Search Intent', id: 'search_intent' },
|
||||
{ label: 'Search News', id: 'search_news' },
|
||||
],
|
||||
value: () => 'search_companies',
|
||||
},
|
||||
{
|
||||
id: 'clientId',
|
||||
title: 'ZoomInfo Client ID',
|
||||
type: 'short-input',
|
||||
placeholder: 'Enter your ZoomInfo OAuth client ID',
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
id: 'clientSecret',
|
||||
title: 'ZoomInfo Client Secret',
|
||||
type: 'short-input',
|
||||
placeholder: 'Enter your ZoomInfo OAuth client secret',
|
||||
password: true,
|
||||
required: true,
|
||||
},
|
||||
|
||||
// Search Companies
|
||||
{
|
||||
id: 'companyName',
|
||||
title: 'Company Name',
|
||||
type: 'short-input',
|
||||
placeholder: 'Acme Corp',
|
||||
condition: { field: 'operation', value: ['search_companies', 'search_contacts'] },
|
||||
},
|
||||
{
|
||||
id: 'companyWebsite',
|
||||
title: 'Company Website',
|
||||
type: 'short-input',
|
||||
placeholder: 'acme.com (comma-separated for multiple)',
|
||||
condition: { field: 'operation', value: 'search_companies' },
|
||||
},
|
||||
{
|
||||
id: 'companyTicker',
|
||||
title: 'Ticker Symbol',
|
||||
type: 'short-input',
|
||||
placeholder: 'ACME',
|
||||
condition: { field: 'operation', value: 'search_companies' },
|
||||
mode: 'advanced',
|
||||
},
|
||||
{
|
||||
id: 'industryCodes',
|
||||
title: 'Industry Codes',
|
||||
type: 'code',
|
||||
placeholder: '["software","saas"]',
|
||||
condition: { field: 'operation', value: ['search_companies', 'search_intent'] },
|
||||
mode: 'advanced',
|
||||
},
|
||||
{
|
||||
id: 'country',
|
||||
title: 'Country',
|
||||
type: 'short-input',
|
||||
placeholder: 'United States',
|
||||
condition: { field: 'operation', value: ['search_companies', 'search_intent'] },
|
||||
mode: 'advanced',
|
||||
},
|
||||
{
|
||||
id: 'state',
|
||||
title: 'State / Province',
|
||||
type: 'short-input',
|
||||
placeholder: 'California',
|
||||
condition: { field: 'operation', value: ['search_companies', 'search_intent'] },
|
||||
mode: 'advanced',
|
||||
},
|
||||
{
|
||||
id: 'metroRegion',
|
||||
title: 'Metro Region',
|
||||
type: 'short-input',
|
||||
placeholder: 'San Francisco Bay Area',
|
||||
condition: { field: 'operation', value: 'search_companies' },
|
||||
mode: 'advanced',
|
||||
},
|
||||
{
|
||||
id: 'revenueMin',
|
||||
title: 'Min Revenue (thousands USD)',
|
||||
type: 'short-input',
|
||||
placeholder: '1000',
|
||||
condition: { field: 'operation', value: 'search_companies' },
|
||||
mode: 'advanced',
|
||||
},
|
||||
{
|
||||
id: 'revenueMax',
|
||||
title: 'Max Revenue (thousands USD)',
|
||||
type: 'short-input',
|
||||
placeholder: '100000',
|
||||
condition: { field: 'operation', value: 'search_companies' },
|
||||
mode: 'advanced',
|
||||
},
|
||||
{
|
||||
id: 'employeeRangeMin',
|
||||
title: 'Min Employees',
|
||||
type: 'short-input',
|
||||
placeholder: '10',
|
||||
condition: { field: 'operation', value: 'search_companies' },
|
||||
mode: 'advanced',
|
||||
},
|
||||
{
|
||||
id: 'employeeRangeMax',
|
||||
title: 'Max Employees',
|
||||
type: 'short-input',
|
||||
placeholder: '5000',
|
||||
condition: { field: 'operation', value: 'search_companies' },
|
||||
mode: 'advanced',
|
||||
},
|
||||
{
|
||||
id: 'excludeDefunctCompanies',
|
||||
title: 'Exclude Defunct Companies',
|
||||
type: 'switch',
|
||||
condition: { field: 'operation', value: 'search_companies' },
|
||||
mode: 'advanced',
|
||||
},
|
||||
|
||||
// Search Contacts
|
||||
{
|
||||
id: 'firstName',
|
||||
title: 'First Name',
|
||||
type: 'short-input',
|
||||
placeholder: 'Jane',
|
||||
condition: { field: 'operation', value: 'search_contacts' },
|
||||
},
|
||||
{
|
||||
id: 'lastName',
|
||||
title: 'Last Name',
|
||||
type: 'short-input',
|
||||
placeholder: 'Doe',
|
||||
condition: { field: 'operation', value: 'search_contacts' },
|
||||
},
|
||||
{
|
||||
id: 'fullName',
|
||||
title: 'Full Name',
|
||||
type: 'short-input',
|
||||
placeholder: 'Jane Doe',
|
||||
condition: { field: 'operation', value: 'search_contacts' },
|
||||
mode: 'advanced',
|
||||
},
|
||||
{
|
||||
id: 'emailAddress',
|
||||
title: 'Email Address',
|
||||
type: 'short-input',
|
||||
placeholder: 'jane@acme.com',
|
||||
condition: { field: 'operation', value: 'search_contacts' },
|
||||
},
|
||||
{
|
||||
id: 'jobTitle',
|
||||
title: 'Job Title',
|
||||
type: 'short-input',
|
||||
placeholder: 'VP of Marketing',
|
||||
condition: { field: 'operation', value: 'search_contacts' },
|
||||
},
|
||||
{
|
||||
id: 'managementLevel',
|
||||
title: 'Management Level',
|
||||
type: 'code',
|
||||
placeholder: '["vp","director","manager"]',
|
||||
condition: { field: 'operation', value: 'search_contacts' },
|
||||
mode: 'advanced',
|
||||
},
|
||||
{
|
||||
id: 'department',
|
||||
title: 'Department',
|
||||
type: 'code',
|
||||
placeholder: '["sales","marketing"]',
|
||||
condition: { field: 'operation', value: 'search_contacts' },
|
||||
mode: 'advanced',
|
||||
},
|
||||
{
|
||||
id: 'companyId',
|
||||
title: 'Company ID',
|
||||
type: 'short-input',
|
||||
placeholder: 'ZoomInfo company ID',
|
||||
condition: { field: 'operation', value: 'search_contacts' },
|
||||
mode: 'advanced',
|
||||
},
|
||||
{
|
||||
id: 'contactAccuracyScoreMin',
|
||||
title: 'Min Accuracy Score',
|
||||
type: 'short-input',
|
||||
placeholder: '70-99',
|
||||
condition: { field: 'operation', value: 'search_contacts' },
|
||||
mode: 'advanced',
|
||||
},
|
||||
{
|
||||
id: 'requiredFields',
|
||||
title: 'Required Fields',
|
||||
type: 'code',
|
||||
placeholder: '["email","phone"]',
|
||||
condition: { field: 'operation', value: ['search_contacts', 'enrich_contacts'] },
|
||||
mode: 'advanced',
|
||||
},
|
||||
{
|
||||
id: 'excludePartialProfiles',
|
||||
title: 'Exclude Partial Profiles',
|
||||
type: 'switch',
|
||||
condition: { field: 'operation', value: 'search_contacts' },
|
||||
mode: 'advanced',
|
||||
},
|
||||
|
||||
// Enrich Companies / Contacts
|
||||
{
|
||||
id: 'matchCompanyInput',
|
||||
title: 'Companies to Enrich (JSON Array)',
|
||||
type: 'code',
|
||||
placeholder: '[{"companyName":"Acme","companyWebsite":"acme.com"}]',
|
||||
condition: { field: 'operation', value: 'enrich_companies' },
|
||||
required: { field: 'operation', value: 'enrich_companies' },
|
||||
},
|
||||
{
|
||||
id: 'matchPersonInput',
|
||||
title: 'Contacts to Enrich (JSON Array)',
|
||||
type: 'code',
|
||||
placeholder: '[{"firstName":"Jane","lastName":"Doe","companyName":"Acme"}]',
|
||||
condition: { field: 'operation', value: 'enrich_contacts' },
|
||||
required: { field: 'operation', value: 'enrich_contacts' },
|
||||
},
|
||||
{
|
||||
id: 'outputFields',
|
||||
title: 'Output Fields',
|
||||
type: 'code',
|
||||
placeholder: '["id","name","website","revenue","employeeCount"]',
|
||||
condition: { field: 'operation', value: ['enrich_companies', 'enrich_contacts'] },
|
||||
mode: 'advanced',
|
||||
},
|
||||
|
||||
// Search Intent
|
||||
{
|
||||
id: 'topics',
|
||||
title: 'Intent Topics',
|
||||
type: 'code',
|
||||
placeholder: '["CRM Software","Marketing Automation"]',
|
||||
condition: { field: 'operation', value: 'search_intent' },
|
||||
required: { field: 'operation', value: 'search_intent' },
|
||||
},
|
||||
{
|
||||
id: 'signalStartDate',
|
||||
title: 'Signal Start Date',
|
||||
type: 'short-input',
|
||||
placeholder: 'YYYY-MM-DD',
|
||||
condition: { field: 'operation', value: 'search_intent' },
|
||||
mode: 'advanced',
|
||||
wandConfig: {
|
||||
enabled: true,
|
||||
prompt:
|
||||
'Generate a date in YYYY-MM-DD format. Return ONLY the date string, no quotes or explanation.',
|
||||
placeholder: 'Describe the date (e.g., "30 days ago")...',
|
||||
generationType: 'timestamp',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'signalEndDate',
|
||||
title: 'Signal End Date',
|
||||
type: 'short-input',
|
||||
placeholder: 'YYYY-MM-DD',
|
||||
condition: { field: 'operation', value: 'search_intent' },
|
||||
mode: 'advanced',
|
||||
wandConfig: {
|
||||
enabled: true,
|
||||
prompt:
|
||||
'Generate a date in YYYY-MM-DD format. Return ONLY the date string, no quotes or explanation.',
|
||||
placeholder: 'Describe the date (e.g., "today")...',
|
||||
generationType: 'timestamp',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'signalScoreMin',
|
||||
title: 'Min Signal Score (60-100)',
|
||||
type: 'short-input',
|
||||
placeholder: '60',
|
||||
condition: { field: 'operation', value: 'search_intent' },
|
||||
mode: 'advanced',
|
||||
},
|
||||
{
|
||||
id: 'signalScoreMax',
|
||||
title: 'Max Signal Score (60-100)',
|
||||
type: 'short-input',
|
||||
placeholder: '100',
|
||||
condition: { field: 'operation', value: 'search_intent' },
|
||||
mode: 'advanced',
|
||||
},
|
||||
{
|
||||
id: 'audienceStrengthMin',
|
||||
title: 'Min Audience Strength (A-E)',
|
||||
type: 'dropdown',
|
||||
options: [
|
||||
{ label: 'A (largest)', id: 'A' },
|
||||
{ label: 'B', id: 'B' },
|
||||
{ label: 'C', id: 'C' },
|
||||
{ label: 'D', id: 'D' },
|
||||
{ label: 'E', id: 'E' },
|
||||
],
|
||||
condition: { field: 'operation', value: 'search_intent' },
|
||||
mode: 'advanced',
|
||||
},
|
||||
{
|
||||
id: 'audienceStrengthMax',
|
||||
title: 'Max Audience Strength (A-E)',
|
||||
type: 'dropdown',
|
||||
options: [
|
||||
{ label: 'A (largest)', id: 'A' },
|
||||
{ label: 'B', id: 'B' },
|
||||
{ label: 'C', id: 'C' },
|
||||
{ label: 'D', id: 'D' },
|
||||
{ label: 'E', id: 'E' },
|
||||
],
|
||||
condition: { field: 'operation', value: 'search_intent' },
|
||||
mode: 'advanced',
|
||||
},
|
||||
{
|
||||
id: 'findRecommendedContacts',
|
||||
title: 'Include Recommended Contacts',
|
||||
type: 'switch',
|
||||
condition: { field: 'operation', value: 'search_intent' },
|
||||
mode: 'advanced',
|
||||
},
|
||||
|
||||
// Search News
|
||||
{
|
||||
id: 'categories',
|
||||
title: 'Categories',
|
||||
type: 'code',
|
||||
placeholder: '["funding","acquisition"]',
|
||||
condition: { field: 'operation', value: 'search_news' },
|
||||
},
|
||||
{
|
||||
id: 'url',
|
||||
title: 'Source URLs',
|
||||
type: 'code',
|
||||
placeholder: '["https://techcrunch.com"]',
|
||||
condition: { field: 'operation', value: 'search_news' },
|
||||
mode: 'advanced',
|
||||
},
|
||||
{
|
||||
id: 'pageDateMin',
|
||||
title: 'Earliest Publish Date',
|
||||
type: 'short-input',
|
||||
placeholder: 'YYYY-MM-DD',
|
||||
condition: { field: 'operation', value: 'search_news' },
|
||||
mode: 'advanced',
|
||||
wandConfig: {
|
||||
enabled: true,
|
||||
prompt:
|
||||
'Generate a date in YYYY-MM-DD format. Return ONLY the date string, no quotes or explanation.',
|
||||
placeholder: 'Describe the date (e.g., "last week")...',
|
||||
generationType: 'timestamp',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'pageDateMax',
|
||||
title: 'Latest Publish Date',
|
||||
type: 'short-input',
|
||||
placeholder: 'YYYY-MM-DD',
|
||||
condition: { field: 'operation', value: 'search_news' },
|
||||
mode: 'advanced',
|
||||
wandConfig: {
|
||||
enabled: true,
|
||||
prompt:
|
||||
'Generate a date in YYYY-MM-DD format. Return ONLY the date string, no quotes or explanation.',
|
||||
placeholder: 'Describe the date (e.g., "today")...',
|
||||
generationType: 'timestamp',
|
||||
},
|
||||
},
|
||||
|
||||
// Sort + Pagination
|
||||
{
|
||||
id: 'sortBy',
|
||||
title: 'Sort By Field',
|
||||
type: 'short-input',
|
||||
placeholder: 'Field name',
|
||||
condition: { field: 'operation', value: ['search_companies', 'search_contacts'] },
|
||||
mode: 'advanced',
|
||||
},
|
||||
{
|
||||
id: 'sortOrder',
|
||||
title: 'Sort Order',
|
||||
type: 'dropdown',
|
||||
options: [
|
||||
{ label: 'Ascending', id: 'asc' },
|
||||
{ label: 'Descending', id: 'desc' },
|
||||
],
|
||||
condition: { field: 'operation', value: ['search_companies', 'search_contacts'] },
|
||||
mode: 'advanced',
|
||||
},
|
||||
{
|
||||
id: 'page',
|
||||
title: 'Page Number',
|
||||
type: 'short-input',
|
||||
placeholder: '1',
|
||||
condition: {
|
||||
field: 'operation',
|
||||
value: ['search_companies', 'search_contacts', 'search_intent', 'search_news'],
|
||||
},
|
||||
mode: 'advanced',
|
||||
},
|
||||
{
|
||||
id: 'rpp',
|
||||
title: 'Results Per Page',
|
||||
type: 'short-input',
|
||||
placeholder: '25 (max 100)',
|
||||
condition: {
|
||||
field: 'operation',
|
||||
value: ['search_companies', 'search_contacts', 'search_intent', 'search_news'],
|
||||
},
|
||||
mode: 'advanced',
|
||||
},
|
||||
],
|
||||
tools: {
|
||||
access: [
|
||||
'zoominfo_search_companies',
|
||||
'zoominfo_search_contacts',
|
||||
'zoominfo_enrich_companies',
|
||||
'zoominfo_enrich_contacts',
|
||||
'zoominfo_search_intent',
|
||||
'zoominfo_search_news',
|
||||
],
|
||||
config: {
|
||||
tool: (params) => {
|
||||
switch (params.operation) {
|
||||
case 'search_companies':
|
||||
return 'zoominfo_search_companies'
|
||||
case 'search_contacts':
|
||||
return 'zoominfo_search_contacts'
|
||||
case 'enrich_companies':
|
||||
return 'zoominfo_enrich_companies'
|
||||
case 'enrich_contacts':
|
||||
return 'zoominfo_enrich_contacts'
|
||||
case 'search_intent':
|
||||
return 'zoominfo_search_intent'
|
||||
case 'search_news':
|
||||
return 'zoominfo_search_news'
|
||||
default:
|
||||
throw new Error(`Invalid ZoomInfo operation: ${params.operation}`)
|
||||
}
|
||||
},
|
||||
params: (params) => {
|
||||
const { operation: _operation, ...rest } = params as Record<string, unknown>
|
||||
const parsed: Record<string, unknown> = { ...rest }
|
||||
|
||||
const toNumber = (key: string) => {
|
||||
const v = parsed[key]
|
||||
if (v === undefined || v === null || v === '') return
|
||||
const n = Number(v)
|
||||
if (Number.isFinite(n)) parsed[key] = n
|
||||
}
|
||||
|
||||
for (const key of [
|
||||
'revenueMin',
|
||||
'revenueMax',
|
||||
'employeeRangeMin',
|
||||
'employeeRangeMax',
|
||||
'contactAccuracyScoreMin',
|
||||
'signalScoreMin',
|
||||
'signalScoreMax',
|
||||
'page',
|
||||
'rpp',
|
||||
]) {
|
||||
toNumber(key)
|
||||
}
|
||||
|
||||
return parsed
|
||||
},
|
||||
},
|
||||
},
|
||||
inputs: {
|
||||
operation: { type: 'string', description: 'ZoomInfo operation to perform' },
|
||||
clientId: { type: 'string', description: 'ZoomInfo OAuth client ID' },
|
||||
clientSecret: { type: 'string', description: 'ZoomInfo OAuth client secret' },
|
||||
companyName: { type: 'string', description: 'Company name' },
|
||||
companyWebsite: { type: 'string', description: 'Company website' },
|
||||
companyTicker: { type: 'string', description: 'Stock ticker' },
|
||||
industryCodes: { type: 'string', description: 'Industry codes' },
|
||||
country: { type: 'string', description: 'Country' },
|
||||
state: { type: 'string', description: 'State' },
|
||||
metroRegion: { type: 'string', description: 'Metro region' },
|
||||
revenueMin: { type: 'number', description: 'Min revenue (thousands USD)' },
|
||||
revenueMax: { type: 'number', description: 'Max revenue (thousands USD)' },
|
||||
employeeRangeMin: { type: 'number', description: 'Min employees' },
|
||||
employeeRangeMax: { type: 'number', description: 'Max employees' },
|
||||
excludeDefunctCompanies: { type: 'boolean', description: 'Exclude defunct companies' },
|
||||
firstName: { type: 'string', description: 'First name' },
|
||||
lastName: { type: 'string', description: 'Last name' },
|
||||
fullName: { type: 'string', description: 'Full name' },
|
||||
emailAddress: { type: 'string', description: 'Email address' },
|
||||
jobTitle: { type: 'string', description: 'Job title' },
|
||||
managementLevel: { type: 'string', description: 'Management level' },
|
||||
department: { type: 'string', description: 'Department' },
|
||||
companyId: { type: 'string', description: 'Company ID' },
|
||||
contactAccuracyScoreMin: { type: 'number', description: 'Min accuracy score' },
|
||||
requiredFields: { type: 'string', description: 'Required fields' },
|
||||
excludePartialProfiles: { type: 'boolean', description: 'Exclude partial profiles' },
|
||||
matchCompanyInput: { type: 'string', description: 'Companies to enrich (JSON array)' },
|
||||
matchPersonInput: { type: 'string', description: 'Contacts to enrich (JSON array)' },
|
||||
outputFields: { type: 'string', description: 'Output fields' },
|
||||
topics: { type: 'string', description: 'Intent topics' },
|
||||
signalStartDate: { type: 'string', description: 'Signal start date' },
|
||||
signalEndDate: { type: 'string', description: 'Signal end date' },
|
||||
signalScoreMin: { type: 'number', description: 'Min signal score' },
|
||||
signalScoreMax: { type: 'number', description: 'Max signal score' },
|
||||
audienceStrengthMin: { type: 'string', description: 'Min audience strength (A-E)' },
|
||||
audienceStrengthMax: { type: 'string', description: 'Max audience strength (A-E)' },
|
||||
findRecommendedContacts: { type: 'boolean', description: 'Include recommended contacts' },
|
||||
categories: { type: 'string', description: 'News categories' },
|
||||
url: { type: 'string', description: 'News source URLs' },
|
||||
pageDateMin: { type: 'string', description: 'Earliest publish date' },
|
||||
pageDateMax: { type: 'string', description: 'Latest publish date' },
|
||||
sortBy: { type: 'string', description: 'Sort field' },
|
||||
sortOrder: { type: 'string', description: 'Sort order' },
|
||||
page: { type: 'number', description: 'Page number' },
|
||||
rpp: { type: 'number', description: 'Results per page' },
|
||||
},
|
||||
outputs: {
|
||||
companies: { type: 'json', description: 'Matching companies (search_companies)' },
|
||||
contacts: { type: 'json', description: 'Matching contacts (search_contacts)' },
|
||||
results: {
|
||||
type: 'json',
|
||||
description: 'Enrichment results (enrich_companies / enrich_contacts)',
|
||||
},
|
||||
signals: { type: 'json', description: 'Intent signals (search_intent)' },
|
||||
articles: { type: 'json', description: 'News articles (search_news)' },
|
||||
totalResults: { type: 'number', description: 'Total matching results across all pages' },
|
||||
currentPage: { type: 'number', description: 'Current page number' },
|
||||
totalPages: { type: 'number', description: 'Total number of pages available' },
|
||||
},
|
||||
}
|
||||
@@ -243,6 +243,7 @@ import { YouTubeBlock } from '@/blocks/blocks/youtube'
|
||||
import { ZendeskBlock } from '@/blocks/blocks/zendesk'
|
||||
import { ZepBlock } from '@/blocks/blocks/zep'
|
||||
import { ZoomBlock } from '@/blocks/blocks/zoom'
|
||||
import { ZoomInfoBlock } from '@/blocks/blocks/zoominfo'
|
||||
import type { BlockConfig } from '@/blocks/types'
|
||||
|
||||
// Registry of all available blocks, alphabetically sorted
|
||||
@@ -513,6 +514,7 @@ export const registry: Record<string, BlockConfig> = {
|
||||
zendesk: ZendeskBlock,
|
||||
zep: ZepBlock,
|
||||
zoom: ZoomBlock,
|
||||
zoominfo: ZoomInfoBlock,
|
||||
}
|
||||
|
||||
export const getBlock = (type: string): BlockConfig | undefined => {
|
||||
|
||||
@@ -4683,6 +4683,36 @@ export function ZoomIcon(props: SVGProps<SVGSVGElement>) {
|
||||
)
|
||||
}
|
||||
|
||||
export function ZoomInfoIcon(props: SVGProps<SVGSVGElement>) {
|
||||
const id = useId()
|
||||
const clipId = `zoominfo-clip_${id}`
|
||||
return (
|
||||
<svg
|
||||
{...props}
|
||||
viewBox='0 0 65 65'
|
||||
fill='none'
|
||||
xmlns='http://www.w3.org/2000/svg'
|
||||
aria-hidden='true'
|
||||
>
|
||||
<g clipPath={`url(#${clipId})`}>
|
||||
<path
|
||||
d='M0.272461 8.27246C0.272461 3.85418 3.85418 0.272461 8.27246 0.272461H56.2725C60.6907 0.272461 64.2725 3.85418 64.2725 8.27246V56.2725C64.2725 60.6907 60.6907 64.2725 56.2725 64.2725H8.27246C3.85418 64.2725 0.272461 60.6907 0.272461 56.2725V8.27246Z'
|
||||
fill='#EA1B15'
|
||||
/>
|
||||
<path
|
||||
d='M44.2725 16.2725H47.7096C48.0212 16.2725 48.2726 16.5239 48.2726 16.8356V25.6993C48.2726 26.011 48.0313 26.2624 47.7196 26.2725H44.8355C44.5239 26.2725 44.2725 26.0211 44.2725 25.7093V23.1162L22.0381 44.2725H27.7095C28.0212 44.2725 28.2725 44.524 28.2725 44.8357V47.7094C28.2725 48.0211 28.0212 48.2726 27.7095 48.2726H16.8355C16.5238 48.2726 16.2725 48.0211 16.2725 47.7094V44.2725L41.4757 20.2725H38.8355C38.5239 20.2725 38.2725 20.0211 38.2725 19.7093V16.8457C38.2725 16.5339 38.5239 16.2825 38.8355 16.2825L44.2725 16.2725ZM24.2725 20.2725L33.7095 20.2624C34.0212 20.2624 34.2725 20.011 34.2725 19.6992V16.8356C34.2725 16.5239 34.0212 16.2725 33.7196 16.2725H16.8556C16.5439 16.2725 16.2926 16.5239 16.2926 16.8356V25.7093C16.2926 26.0211 16.5439 26.2725 16.8556 26.2725H19.7195C20.0312 26.2725 20.2725 26.0211 20.2725 25.7093V20.2725H24.2725ZM44.2725 44.2725H32.8355C32.5238 44.2725 32.2725 44.524 32.2725 44.8357V47.7094C32.2725 48.0211 32.5238 48.2726 32.8355 48.2726H47.7096C48.0212 48.2726 48.2726 48.0211 48.2726 47.7094V38.7888C48.2726 38.4771 48.0212 38.2257 47.7096 38.2257H44.8355C44.5239 38.2257 44.2725 38.4771 44.2725 38.7888V44.2725Z'
|
||||
fill='white'
|
||||
/>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id={clipId}>
|
||||
<rect width='64' height='64' fill='white' transform='translate(0.272461 0.272461)' />
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
|
||||
export function SendgridIcon(props: SVGProps<SVGSVGElement>) {
|
||||
return (
|
||||
<svg
|
||||
|
||||
@@ -18,7 +18,7 @@ export const apolloAccountBulkCreateTool: ToolConfig<
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'hidden',
|
||||
visibility: 'user-only',
|
||||
description: 'Apollo API key (master key required)',
|
||||
},
|
||||
accounts: {
|
||||
|
||||
@@ -18,7 +18,7 @@ export const apolloAccountBulkUpdateTool: ToolConfig<
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'hidden',
|
||||
visibility: 'user-only',
|
||||
description: 'Apollo API key (master key required)',
|
||||
},
|
||||
account_ids: {
|
||||
@@ -40,6 +40,12 @@ export const apolloAccountBulkUpdateTool: ToolConfig<
|
||||
visibility: 'user-or-llm',
|
||||
description: 'When using account_ids, apply this owner to all accounts',
|
||||
},
|
||||
account_stage_id: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'When using account_ids, apply this account stage to all accounts',
|
||||
},
|
||||
account_attributes: {
|
||||
type: 'json',
|
||||
required: false,
|
||||
@@ -71,6 +77,7 @@ export const apolloAccountBulkUpdateTool: ToolConfig<
|
||||
}
|
||||
if (params.name) body.name = params.name
|
||||
if (params.owner_id) body.owner_id = params.owner_id
|
||||
if (params.account_stage_id) body.account_stage_id = params.account_stage_id
|
||||
if (params.account_attributes) {
|
||||
if (Array.isArray(params.account_attributes)) {
|
||||
if (params.account_attributes.length > 0) {
|
||||
@@ -83,7 +90,8 @@ export const apolloAccountBulkUpdateTool: ToolConfig<
|
||||
body.account_attributes = params.account_attributes
|
||||
}
|
||||
}
|
||||
const hasUpdateFields = body.account_attributes || body.name || body.owner_id
|
||||
const hasUpdateFields =
|
||||
body.account_attributes || body.name || body.owner_id || body.account_stage_id
|
||||
if (!hasUpdateFields) {
|
||||
throw new Error(
|
||||
'Apollo account bulk update requires update fields. Provide account_attributes (array of per-account updates with id, or single object paired with account_ids), or pair account_ids with name/owner_id to apply uniformly.'
|
||||
@@ -116,25 +124,53 @@ export const apolloAccountBulkUpdateTool: ToolConfig<
|
||||
}
|
||||
|
||||
const data = await response.json()
|
||||
const accounts = Array.isArray(data?.accounts) ? data.accounts : []
|
||||
const entityProgressJob = data?.entity_progress_job ?? null
|
||||
const accountIds = Array.isArray(data?.account_ids)
|
||||
? data.account_ids
|
||||
: accounts.map((a: { id?: unknown }) => a?.id).filter((id: unknown) => typeof id === 'string')
|
||||
const jobId =
|
||||
typeof data?.job_id === 'string'
|
||||
? data.job_id
|
||||
: typeof entityProgressJob?.id === 'string'
|
||||
? entityProgressJob.id
|
||||
: null
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
message: data.message ?? null,
|
||||
account_ids: data.account_ids ?? [],
|
||||
accounts,
|
||||
account_ids: accountIds,
|
||||
entity_progress_job: entityProgressJob,
|
||||
job_id: jobId,
|
||||
message: data?.message ?? null,
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
message: {
|
||||
type: 'string',
|
||||
description: 'Confirmation message from Apollo',
|
||||
optional: true,
|
||||
accounts: {
|
||||
type: 'json',
|
||||
description: 'Updated accounts (synchronous response): [{id, account_stage_id, ...}]',
|
||||
},
|
||||
account_ids: {
|
||||
type: 'json',
|
||||
description: 'IDs of accounts that were updated',
|
||||
},
|
||||
entity_progress_job: {
|
||||
type: 'json',
|
||||
description: 'Async job descriptor (when async=true is passed with account_ids)',
|
||||
optional: true,
|
||||
},
|
||||
job_id: {
|
||||
type: 'string',
|
||||
description: 'Async job ID extracted from entity_progress_job',
|
||||
optional: true,
|
||||
},
|
||||
message: {
|
||||
type: 'string',
|
||||
description: 'Optional confirmation message from Apollo',
|
||||
optional: true,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
@@ -14,7 +14,7 @@ export const apolloAccountCreateTool: ToolConfig<
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'hidden',
|
||||
visibility: 'user-only',
|
||||
description: 'Apollo API key (master key required)',
|
||||
},
|
||||
name: {
|
||||
|
||||
@@ -15,7 +15,7 @@ export const apolloAccountSearchTool: ToolConfig<
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'hidden',
|
||||
visibility: 'user-only',
|
||||
description: 'Apollo API key (master key required)',
|
||||
},
|
||||
q_organization_name: {
|
||||
|
||||
@@ -14,7 +14,7 @@ export const apolloAccountUpdateTool: ToolConfig<
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'hidden',
|
||||
visibility: 'user-only',
|
||||
description: 'Apollo API key',
|
||||
},
|
||||
account_id: {
|
||||
|
||||
@@ -18,7 +18,7 @@ export const apolloContactBulkCreateTool: ToolConfig<
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'hidden',
|
||||
visibility: 'user-only',
|
||||
description: 'Apollo API key (master key required)',
|
||||
},
|
||||
contacts: {
|
||||
|
||||
@@ -18,7 +18,7 @@ export const apolloContactBulkUpdateTool: ToolConfig<
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'hidden',
|
||||
visibility: 'user-only',
|
||||
description: 'Apollo API key (master key required)',
|
||||
},
|
||||
contact_ids: {
|
||||
@@ -95,25 +95,44 @@ export const apolloContactBulkUpdateTool: ToolConfig<
|
||||
}
|
||||
|
||||
const data = await response.json()
|
||||
const contacts = Array.isArray(data?.contacts) ? data.contacts : []
|
||||
const entityProgressJob = data?.entity_progress_job ?? null
|
||||
const jobId =
|
||||
typeof data?.job_id === 'string'
|
||||
? data.job_id
|
||||
: typeof entityProgressJob?.id === 'string'
|
||||
? entityProgressJob.id
|
||||
: null
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
message: data.message ?? null,
|
||||
job_id: data.job_id ?? null,
|
||||
contacts,
|
||||
entity_progress_job: entityProgressJob,
|
||||
job_id: jobId,
|
||||
message: data?.message ?? null,
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
message: {
|
||||
type: 'string',
|
||||
description: 'Confirmation message from Apollo',
|
||||
contacts: {
|
||||
type: 'json',
|
||||
description: 'Updated contacts (synchronous response, ≤100 contacts)',
|
||||
},
|
||||
entity_progress_job: {
|
||||
type: 'json',
|
||||
description: 'Async job descriptor (>100 contacts or async=true): {id, status, ...}',
|
||||
optional: true,
|
||||
},
|
||||
job_id: {
|
||||
type: 'string',
|
||||
description: 'Async job ID (returned for >100 contacts)',
|
||||
description: 'Async job ID extracted from entity_progress_job',
|
||||
optional: true,
|
||||
},
|
||||
message: {
|
||||
type: 'string',
|
||||
description: 'Optional confirmation message from Apollo',
|
||||
optional: true,
|
||||
},
|
||||
},
|
||||
|
||||
@@ -14,7 +14,7 @@ export const apolloContactCreateTool: ToolConfig<
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'hidden',
|
||||
visibility: 'user-only',
|
||||
description: 'Apollo API key',
|
||||
},
|
||||
first_name: {
|
||||
|
||||
@@ -14,7 +14,7 @@ export const apolloContactSearchTool: ToolConfig<
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'hidden',
|
||||
visibility: 'user-only',
|
||||
description: 'Apollo API key',
|
||||
},
|
||||
q_keywords: {
|
||||
|
||||
@@ -14,7 +14,7 @@ export const apolloContactUpdateTool: ToolConfig<
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'hidden',
|
||||
visibility: 'user-only',
|
||||
description: 'Apollo API key',
|
||||
},
|
||||
contact_id: {
|
||||
@@ -124,8 +124,7 @@ export const apolloContactUpdateTool: ToolConfig<
|
||||
type: 'json',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description:
|
||||
'Custom field values keyed by custom field ID (accepted by Apollo but not officially documented for PATCH /contacts/{id})',
|
||||
description: 'Custom field values keyed by custom field ID',
|
||||
},
|
||||
},
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@ export const apolloEmailAccountsTool: ToolConfig<
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'hidden',
|
||||
visibility: 'user-only',
|
||||
description: 'Apollo API key (master key required)',
|
||||
},
|
||||
},
|
||||
|
||||
@@ -17,7 +17,7 @@ export const apolloOpportunityCreateTool: ToolConfig<
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'hidden',
|
||||
visibility: 'user-only',
|
||||
description: 'Apollo API key (master key required)',
|
||||
},
|
||||
name: {
|
||||
|
||||
@@ -14,7 +14,7 @@ export const apolloOpportunityGetTool: ToolConfig<
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'hidden',
|
||||
visibility: 'user-only',
|
||||
description: 'Apollo API key',
|
||||
},
|
||||
opportunity_id: {
|
||||
|
||||
@@ -17,7 +17,7 @@ export const apolloOpportunitySearchTool: ToolConfig<
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'hidden',
|
||||
visibility: 'user-only',
|
||||
description: 'Apollo API key',
|
||||
},
|
||||
sort_by_field: {
|
||||
|
||||
@@ -17,7 +17,7 @@ export const apolloOpportunityUpdateTool: ToolConfig<
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'hidden',
|
||||
visibility: 'user-only',
|
||||
description: 'Apollo API key',
|
||||
},
|
||||
opportunity_id: {
|
||||
|
||||
@@ -17,29 +17,32 @@ export const apolloOrganizationBulkEnrichTool: ToolConfig<
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'hidden',
|
||||
visibility: 'user-only',
|
||||
description: 'Apollo API key',
|
||||
},
|
||||
organizations: {
|
||||
domains: {
|
||||
type: 'array',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description:
|
||||
'Array of organizations to enrich (max 10). Each item requires `name` and may include `domain` (e.g., [{"name": "Example Corp", "domain": "example.com"}])',
|
||||
'Array of company domains to enrich (max 10, no www. or @, e.g., ["apollo.io", "stripe.com"])',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: 'https://api.apollo.io/api/v1/organizations/bulk_enrich',
|
||||
url: (params: ApolloOrganizationBulkEnrichParams) => {
|
||||
const qs = new URLSearchParams()
|
||||
for (const domain of params.domains.slice(0, 10)) {
|
||||
const trimmed = typeof domain === 'string' ? domain.trim() : ''
|
||||
if (trimmed) qs.append('domains[]', trimmed)
|
||||
}
|
||||
return `https://api.apollo.io/api/v1/organizations/bulk_enrich?${qs.toString()}`
|
||||
},
|
||||
method: 'POST',
|
||||
headers: (params: ApolloOrganizationBulkEnrichParams) => ({
|
||||
'Content-Type': 'application/json',
|
||||
'Cache-Control': 'no-cache',
|
||||
'X-Api-Key': params.apiKey,
|
||||
}),
|
||||
body: (params: ApolloOrganizationBulkEnrichParams) => ({
|
||||
organizations: params.organizations.slice(0, 10),
|
||||
}),
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response) => {
|
||||
|
||||
@@ -17,7 +17,7 @@ export const apolloOrganizationEnrichTool: ToolConfig<
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'hidden',
|
||||
visibility: 'user-only',
|
||||
description: 'Apollo API key',
|
||||
},
|
||||
domain: {
|
||||
@@ -29,20 +29,19 @@ export const apolloOrganizationEnrichTool: ToolConfig<
|
||||
},
|
||||
|
||||
request: {
|
||||
url: 'https://api.apollo.io/api/v1/organizations/enrich',
|
||||
method: 'POST',
|
||||
headers: (params: ApolloOrganizationEnrichParams) => ({
|
||||
'Content-Type': 'application/json',
|
||||
'Cache-Control': 'no-cache',
|
||||
'X-Api-Key': params.apiKey,
|
||||
}),
|
||||
body: (params: ApolloOrganizationEnrichParams) => {
|
||||
url: (params: ApolloOrganizationEnrichParams) => {
|
||||
const domain = params.domain?.trim()
|
||||
if (!domain) {
|
||||
throw new Error('domain is required for organization enrichment')
|
||||
}
|
||||
return { domain }
|
||||
const qs = new URLSearchParams({ domain })
|
||||
return `https://api.apollo.io/api/v1/organizations/enrich?${qs.toString()}`
|
||||
},
|
||||
method: 'GET',
|
||||
headers: (params: ApolloOrganizationEnrichParams) => ({
|
||||
'Cache-Control': 'no-cache',
|
||||
'X-Api-Key': params.apiKey,
|
||||
}),
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response) => {
|
||||
|
||||
@@ -17,7 +17,7 @@ export const apolloOrganizationSearchTool: ToolConfig<
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'hidden',
|
||||
visibility: 'user-only',
|
||||
description: 'Apollo API key',
|
||||
},
|
||||
organization_locations: {
|
||||
@@ -78,43 +78,32 @@ export const apolloOrganizationSearchTool: ToolConfig<
|
||||
},
|
||||
|
||||
request: {
|
||||
url: 'https://api.apollo.io/api/v1/mixed_companies/search',
|
||||
url: (params: ApolloOrganizationSearchParams) => {
|
||||
const qs = new URLSearchParams()
|
||||
qs.set('page', String(params.page || 1))
|
||||
qs.set('per_page', String(Math.min(params.per_page || 25, 100)))
|
||||
|
||||
const appendArray = (key: string, values?: string[]) => {
|
||||
if (!values?.length) return
|
||||
for (const v of values) {
|
||||
if (typeof v === 'string' && v) qs.append(`${key}[]`, v)
|
||||
}
|
||||
}
|
||||
appendArray('organization_locations', params.organization_locations)
|
||||
appendArray('organization_not_locations', params.organization_not_locations)
|
||||
appendArray('organization_num_employees_ranges', params.organization_num_employees_ranges)
|
||||
appendArray('q_organization_keyword_tags', params.q_organization_keyword_tags)
|
||||
appendArray('organization_ids', params.organization_ids)
|
||||
appendArray('q_organization_domains_list', params.q_organization_domains_list)
|
||||
if (params.q_organization_name) qs.set('q_organization_name', params.q_organization_name)
|
||||
|
||||
return `https://api.apollo.io/api/v1/mixed_companies/search?${qs.toString()}`
|
||||
},
|
||||
method: 'POST',
|
||||
headers: (params: ApolloOrganizationSearchParams) => ({
|
||||
'Content-Type': 'application/json',
|
||||
'Cache-Control': 'no-cache',
|
||||
'X-Api-Key': params.apiKey,
|
||||
}),
|
||||
body: (params: ApolloOrganizationSearchParams) => {
|
||||
const body: Record<string, unknown> = {
|
||||
page: params.page || 1,
|
||||
per_page: Math.min(params.per_page || 25, 100),
|
||||
}
|
||||
|
||||
if (params.organization_locations?.length) {
|
||||
body.organization_locations = params.organization_locations
|
||||
}
|
||||
if (params.organization_not_locations?.length) {
|
||||
body.organization_not_locations = params.organization_not_locations
|
||||
}
|
||||
if (params.organization_num_employees_ranges?.length) {
|
||||
body.organization_num_employees_ranges = params.organization_num_employees_ranges
|
||||
}
|
||||
if (params.q_organization_keyword_tags?.length) {
|
||||
body.q_organization_keyword_tags = params.q_organization_keyword_tags
|
||||
}
|
||||
if (params.q_organization_name) {
|
||||
body.q_organization_name = params.q_organization_name
|
||||
}
|
||||
if (params.organization_ids?.length) {
|
||||
body.organization_ids = params.organization_ids
|
||||
}
|
||||
if (params.q_organization_domains_list?.length) {
|
||||
body.q_organization_domains_list = params.q_organization_domains_list
|
||||
}
|
||||
|
||||
return body
|
||||
},
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response) => {
|
||||
|
||||
@@ -17,7 +17,7 @@ export const apolloPeopleBulkEnrichTool: ToolConfig<
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'hidden',
|
||||
visibility: 'user-only',
|
||||
description: 'Apollo API key',
|
||||
},
|
||||
people: {
|
||||
|
||||
@@ -14,7 +14,7 @@ export const apolloPeopleEnrichTool: ToolConfig<
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'hidden',
|
||||
visibility: 'user-only',
|
||||
description: 'Apollo API key',
|
||||
},
|
||||
first_name: {
|
||||
@@ -95,6 +95,15 @@ export const apolloPeopleEnrichTool: ToolConfig<
|
||||
request: {
|
||||
url: (params: ApolloPeopleEnrichParams) => {
|
||||
const qs = new URLSearchParams()
|
||||
if (params.first_name) qs.set('first_name', params.first_name)
|
||||
if (params.last_name) qs.set('last_name', params.last_name)
|
||||
if (params.name) qs.set('name', params.name)
|
||||
if (params.email) qs.set('email', params.email)
|
||||
if (params.hashed_email) qs.set('hashed_email', params.hashed_email)
|
||||
if (params.id) qs.set('id', params.id)
|
||||
if (params.organization_name) qs.set('organization_name', params.organization_name)
|
||||
if (params.domain) qs.set('domain', params.domain)
|
||||
if (params.linkedin_url) qs.set('linkedin_url', params.linkedin_url)
|
||||
if (params.reveal_personal_emails !== undefined) {
|
||||
qs.set('reveal_personal_emails', String(params.reveal_personal_emails))
|
||||
}
|
||||
@@ -109,25 +118,9 @@ export const apolloPeopleEnrichTool: ToolConfig<
|
||||
},
|
||||
method: 'POST',
|
||||
headers: (params: ApolloPeopleEnrichParams) => ({
|
||||
'Content-Type': 'application/json',
|
||||
'Cache-Control': 'no-cache',
|
||||
'X-Api-Key': params.apiKey,
|
||||
}),
|
||||
body: (params: ApolloPeopleEnrichParams) => {
|
||||
const body: Record<string, unknown> = {}
|
||||
|
||||
if (params.first_name) body.first_name = params.first_name
|
||||
if (params.last_name) body.last_name = params.last_name
|
||||
if (params.name) body.name = params.name
|
||||
if (params.email) body.email = params.email
|
||||
if (params.hashed_email) body.hashed_email = params.hashed_email
|
||||
if (params.id) body.id = params.id
|
||||
if (params.organization_name) body.organization_name = params.organization_name
|
||||
if (params.domain) body.domain = params.domain
|
||||
if (params.linkedin_url) body.linkedin_url = params.linkedin_url
|
||||
|
||||
return body
|
||||
},
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response) => {
|
||||
|
||||
@@ -14,7 +14,7 @@ export const apolloPeopleSearchTool: ToolConfig<
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'hidden',
|
||||
visibility: 'user-only',
|
||||
description: 'Apollo API key',
|
||||
},
|
||||
person_titles: {
|
||||
@@ -165,13 +165,17 @@ export const apolloPeopleSearchTool: ToolConfig<
|
||||
|
||||
const data = await response.json()
|
||||
|
||||
// The legacy /mixed_people/search endpoint nests pagination under `pagination`,
|
||||
// while api_search returns these top-level. Read both shapes defensively.
|
||||
const pagination = data.pagination ?? {}
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
people: data.people || [],
|
||||
page: data.pagination?.page || 1,
|
||||
per_page: data.pagination?.per_page || 25,
|
||||
total_entries: data.pagination?.total_entries || 0,
|
||||
page: pagination.page ?? data.page ?? 1,
|
||||
per_page: pagination.per_page ?? data.per_page ?? 25,
|
||||
total_entries: pagination.total_entries ?? data.total_entries ?? 0,
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
@@ -17,7 +17,7 @@ export const apolloSequenceAddContactsTool: ToolConfig<
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'hidden',
|
||||
visibility: 'user-only',
|
||||
description: 'Apollo API key (master key required)',
|
||||
},
|
||||
sequence_id: {
|
||||
@@ -128,15 +128,12 @@ export const apolloSequenceAddContactsTool: ToolConfig<
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params: ApolloSequenceAddContactsParams) =>
|
||||
`https://api.apollo.io/api/v1/emailer_campaigns/${params.sequence_id.trim()}/add_contact_ids`,
|
||||
method: 'POST',
|
||||
headers: (params: ApolloSequenceAddContactsParams) => ({
|
||||
'Content-Type': 'application/json',
|
||||
'Cache-Control': 'no-cache',
|
||||
'X-Api-Key': params.apiKey,
|
||||
}),
|
||||
body: (params: ApolloSequenceAddContactsParams) => {
|
||||
/**
|
||||
* Apollo documents every field for this endpoint as a query parameter (no request body),
|
||||
* so `contact_ids[]`/`label_names[]` are appended to the query string alongside the scalar
|
||||
* settings, matching the documented contract.
|
||||
*/
|
||||
url: (params: ApolloSequenceAddContactsParams) => {
|
||||
const hasContactIds = !!params.contact_ids?.length
|
||||
const hasLabelNames = !!params.label_names?.length
|
||||
if (!hasContactIds && !hasLabelNames) {
|
||||
@@ -144,43 +141,67 @@ export const apolloSequenceAddContactsTool: ToolConfig<
|
||||
'Apollo sequence add requires either contact_ids or label_names to be provided'
|
||||
)
|
||||
}
|
||||
const body: Record<string, unknown> = {
|
||||
emailer_campaign_id: params.sequence_id,
|
||||
send_email_from_email_account_id: params.send_email_from_email_account_id,
|
||||
const qs = new URLSearchParams()
|
||||
qs.set('emailer_campaign_id', params.sequence_id)
|
||||
qs.set('send_email_from_email_account_id', params.send_email_from_email_account_id)
|
||||
for (const id of params.contact_ids ?? []) {
|
||||
if (typeof id === 'string' && id.length > 0) qs.append('contact_ids[]', id)
|
||||
}
|
||||
for (const name of params.label_names ?? []) {
|
||||
if (typeof name === 'string' && name.length > 0) qs.append('label_names[]', name)
|
||||
}
|
||||
if (hasContactIds) body.contact_ids = params.contact_ids
|
||||
if (hasLabelNames) body.label_names = params.label_names
|
||||
if (params.send_email_from_email_address) {
|
||||
body.send_email_from_email_address = params.send_email_from_email_address
|
||||
qs.set('send_email_from_email_address', params.send_email_from_email_address)
|
||||
}
|
||||
if (params.sequence_no_email !== undefined) {
|
||||
qs.set('sequence_no_email', String(params.sequence_no_email))
|
||||
}
|
||||
if (params.sequence_no_email !== undefined) body.sequence_no_email = params.sequence_no_email
|
||||
if (params.sequence_unverified_email !== undefined) {
|
||||
body.sequence_unverified_email = params.sequence_unverified_email
|
||||
qs.set('sequence_unverified_email', String(params.sequence_unverified_email))
|
||||
}
|
||||
if (params.sequence_job_change !== undefined) {
|
||||
body.sequence_job_change = params.sequence_job_change
|
||||
qs.set('sequence_job_change', String(params.sequence_job_change))
|
||||
}
|
||||
if (params.sequence_active_in_other_campaigns !== undefined) {
|
||||
body.sequence_active_in_other_campaigns = params.sequence_active_in_other_campaigns
|
||||
qs.set(
|
||||
'sequence_active_in_other_campaigns',
|
||||
String(params.sequence_active_in_other_campaigns)
|
||||
)
|
||||
}
|
||||
if (params.sequence_finished_in_other_campaigns !== undefined) {
|
||||
body.sequence_finished_in_other_campaigns = params.sequence_finished_in_other_campaigns
|
||||
qs.set(
|
||||
'sequence_finished_in_other_campaigns',
|
||||
String(params.sequence_finished_in_other_campaigns)
|
||||
)
|
||||
}
|
||||
if (params.sequence_same_company_in_same_campaign !== undefined) {
|
||||
body.sequence_same_company_in_same_campaign = params.sequence_same_company_in_same_campaign
|
||||
qs.set(
|
||||
'sequence_same_company_in_same_campaign',
|
||||
String(params.sequence_same_company_in_same_campaign)
|
||||
)
|
||||
}
|
||||
if (params.contacts_without_ownership_permission !== undefined) {
|
||||
body.contacts_without_ownership_permission = params.contacts_without_ownership_permission
|
||||
qs.set(
|
||||
'contacts_without_ownership_permission',
|
||||
String(params.contacts_without_ownership_permission)
|
||||
)
|
||||
}
|
||||
if (params.add_if_in_queue !== undefined) {
|
||||
qs.set('add_if_in_queue', String(params.add_if_in_queue))
|
||||
}
|
||||
if (params.add_if_in_queue !== undefined) body.add_if_in_queue = params.add_if_in_queue
|
||||
if (params.contact_verification_skipped !== undefined) {
|
||||
body.contact_verification_skipped = params.contact_verification_skipped
|
||||
qs.set('contact_verification_skipped', String(params.contact_verification_skipped))
|
||||
}
|
||||
if (params.user_id) body.user_id = params.user_id
|
||||
if (params.status) body.status = params.status
|
||||
if (params.auto_unpause_at) body.auto_unpause_at = params.auto_unpause_at
|
||||
return body
|
||||
if (params.user_id) qs.set('user_id', params.user_id)
|
||||
if (params.status) qs.set('status', params.status)
|
||||
if (params.auto_unpause_at) qs.set('auto_unpause_at', params.auto_unpause_at)
|
||||
return `https://api.apollo.io/api/v1/emailer_campaigns/${params.sequence_id.trim()}/add_contact_ids?${qs.toString()}`
|
||||
},
|
||||
method: 'POST',
|
||||
headers: (params: ApolloSequenceAddContactsParams) => ({
|
||||
'Cache-Control': 'no-cache',
|
||||
'X-Api-Key': params.apiKey,
|
||||
}),
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response, params?: ApolloSequenceAddContactsParams) => {
|
||||
|
||||
@@ -14,7 +14,7 @@ export const apolloSequenceSearchTool: ToolConfig<
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'hidden',
|
||||
visibility: 'user-only',
|
||||
description: 'Apollo API key (master key required)',
|
||||
},
|
||||
q_name: {
|
||||
@@ -38,21 +38,18 @@ export const apolloSequenceSearchTool: ToolConfig<
|
||||
},
|
||||
|
||||
request: {
|
||||
url: 'https://api.apollo.io/api/v1/emailer_campaigns/search',
|
||||
url: (params: ApolloSequenceSearchParams) => {
|
||||
const qs = new URLSearchParams()
|
||||
qs.set('page', String(params.page || 1))
|
||||
qs.set('per_page', String(Math.min(params.per_page || 25, 100)))
|
||||
if (params.q_name) qs.set('q_name', params.q_name)
|
||||
return `https://api.apollo.io/api/v1/emailer_campaigns/search?${qs.toString()}`
|
||||
},
|
||||
method: 'POST',
|
||||
headers: (params: ApolloSequenceSearchParams) => ({
|
||||
'Content-Type': 'application/json',
|
||||
'Cache-Control': 'no-cache',
|
||||
'X-Api-Key': params.apiKey,
|
||||
}),
|
||||
body: (params: ApolloSequenceSearchParams) => {
|
||||
const body: Record<string, unknown> = {
|
||||
page: params.page || 1,
|
||||
per_page: Math.min(params.per_page || 25, 100),
|
||||
}
|
||||
if (params.q_name) body.q_name = params.q_name
|
||||
return body
|
||||
},
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response) => {
|
||||
|
||||
@@ -11,7 +11,7 @@ export const apolloTaskCreateTool: ToolConfig<ApolloTaskCreateParams, ApolloTask
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'hidden',
|
||||
visibility: 'user-only',
|
||||
description: 'Apollo API key (master key required)',
|
||||
},
|
||||
user_id: {
|
||||
|
||||
@@ -11,7 +11,7 @@ export const apolloTaskSearchTool: ToolConfig<ApolloTaskSearchParams, ApolloTask
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'hidden',
|
||||
visibility: 'user-only',
|
||||
description: 'Apollo API key (master key required)',
|
||||
},
|
||||
sort_by_field: {
|
||||
@@ -42,22 +42,23 @@ export const apolloTaskSearchTool: ToolConfig<ApolloTaskSearchParams, ApolloTask
|
||||
},
|
||||
|
||||
request: {
|
||||
url: 'https://api.apollo.io/api/v1/tasks/search',
|
||||
url: (params: ApolloTaskSearchParams) => {
|
||||
const qs = new URLSearchParams()
|
||||
qs.set('page', String(params.page || 1))
|
||||
qs.set('per_page', String(Math.min(params.per_page || 25, 100)))
|
||||
if (params.sort_by_field) qs.set('sort_by_field', params.sort_by_field)
|
||||
if (params.open_factor_names?.length) {
|
||||
for (const name of params.open_factor_names) {
|
||||
if (typeof name === 'string' && name) qs.append('open_factor_names[]', name)
|
||||
}
|
||||
}
|
||||
return `https://api.apollo.io/api/v1/tasks/search?${qs.toString()}`
|
||||
},
|
||||
method: 'POST',
|
||||
headers: (params: ApolloTaskSearchParams) => ({
|
||||
'Content-Type': 'application/json',
|
||||
'Cache-Control': 'no-cache',
|
||||
'X-Api-Key': params.apiKey,
|
||||
}),
|
||||
body: (params: ApolloTaskSearchParams) => {
|
||||
const body: Record<string, unknown> = {
|
||||
page: params.page || 1,
|
||||
per_page: Math.min(params.per_page || 25, 100),
|
||||
}
|
||||
if (params.sort_by_field) body.sort_by_field = params.sort_by_field
|
||||
if (params.open_factor_names?.length) body.open_factor_names = params.open_factor_names
|
||||
return body
|
||||
},
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response) => {
|
||||
|
||||
@@ -229,7 +229,7 @@ export interface ApolloOrganizationEnrichResponse extends ToolResponse {
|
||||
|
||||
// Bulk Organization Enrichment Types
|
||||
export interface ApolloOrganizationBulkEnrichParams extends ApolloBaseParams {
|
||||
organizations: Array<{ name: string; domain?: string }>
|
||||
domains: string[]
|
||||
}
|
||||
|
||||
export interface ApolloOrganizationBulkEnrichResponse extends ToolResponse {
|
||||
@@ -344,8 +344,10 @@ export interface ApolloContactBulkUpdateParams extends ApolloBaseParams {
|
||||
|
||||
export interface ApolloContactBulkUpdateResponse extends ToolResponse {
|
||||
output: {
|
||||
message: string | null
|
||||
contacts: ApolloContact[]
|
||||
entity_progress_job: Record<string, unknown> | null
|
||||
job_id: string | null
|
||||
message: string | null
|
||||
}
|
||||
}
|
||||
|
||||
@@ -466,14 +468,18 @@ export interface ApolloAccountBulkUpdateParams extends ApolloBaseParams {
|
||||
account_ids?: string[]
|
||||
name?: string
|
||||
owner_id?: string
|
||||
account_stage_id?: string
|
||||
account_attributes?: Array<{ id: string; [key: string]: unknown }> | Record<string, unknown>
|
||||
async?: boolean
|
||||
}
|
||||
|
||||
export interface ApolloAccountBulkUpdateResponse extends ToolResponse {
|
||||
output: {
|
||||
message: string | null
|
||||
accounts: ApolloAccount[]
|
||||
account_ids: string[]
|
||||
entity_progress_job: Record<string, unknown> | null
|
||||
job_id: string | null
|
||||
message: string | null
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -49,6 +49,7 @@ export const findEmailFromLinkedInTool: ToolConfig<
|
||||
success: false,
|
||||
error:
|
||||
(errorData as Record<string, string>).message ||
|
||||
(errorData as Record<string, string>).error ||
|
||||
`Findymail API error: ${response.status} ${response.statusText}`,
|
||||
output: { contact: null },
|
||||
}
|
||||
|
||||
@@ -54,6 +54,7 @@ export const findEmailFromNameTool: ToolConfig<
|
||||
success: false,
|
||||
error:
|
||||
(errorData as Record<string, string>).message ||
|
||||
(errorData as Record<string, string>).error ||
|
||||
`Findymail API error: ${response.status} ${response.statusText}`,
|
||||
output: { contact: null },
|
||||
}
|
||||
|
||||
@@ -55,6 +55,7 @@ export const findEmailsByDomainTool: ToolConfig<
|
||||
success: false,
|
||||
error:
|
||||
(errorData as Record<string, string>).message ||
|
||||
(errorData as Record<string, string>).error ||
|
||||
`Findymail API error: ${response.status} ${response.statusText}`,
|
||||
output: { contacts: [] },
|
||||
}
|
||||
|
||||
@@ -68,6 +68,7 @@ export const findEmployeesTool: ToolConfig<
|
||||
success: false,
|
||||
error:
|
||||
(errorData as Record<string, string>).message ||
|
||||
(errorData as Record<string, string>).error ||
|
||||
`Findymail API error: ${response.status} ${response.statusText}`,
|
||||
output: { employees: [] },
|
||||
}
|
||||
|
||||
@@ -42,6 +42,7 @@ export const findPhoneTool: ToolConfig<FindymailFindPhoneParams, FindymailFindPh
|
||||
success: false,
|
||||
error:
|
||||
(errorData as Record<string, string>).message ||
|
||||
(errorData as Record<string, string>).error ||
|
||||
`Findymail API error: ${response.status} ${response.statusText}`,
|
||||
output: { phone: null, line_type: null },
|
||||
}
|
||||
|
||||
@@ -62,6 +62,7 @@ export const getCompanyTool: ToolConfig<FindymailGetCompanyParams, FindymailGetC
|
||||
success: false,
|
||||
error:
|
||||
(errorData as Record<string, string>).message ||
|
||||
(errorData as Record<string, string>).error ||
|
||||
`Findymail API error: ${response.status} ${response.statusText}`,
|
||||
output: {
|
||||
name: null,
|
||||
|
||||
@@ -35,6 +35,7 @@ export const getCreditsTool: ToolConfig<FindymailGetCreditsParams, FindymailGetC
|
||||
success: false,
|
||||
error:
|
||||
(errorData as Record<string, string>).message ||
|
||||
(errorData as Record<string, string>).error ||
|
||||
`Findymail API error: ${response.status} ${response.statusText}`,
|
||||
output: { credits: 0, verifier_credits: 0 },
|
||||
}
|
||||
|
||||
@@ -61,6 +61,7 @@ export const lookupTechnologiesTool: ToolConfig<
|
||||
success: false,
|
||||
error:
|
||||
(errorData as Record<string, string>).message ||
|
||||
(errorData as Record<string, string>).error ||
|
||||
`Findymail API error: ${response.status} ${response.statusText}`,
|
||||
output: { domain: '', technologies: [] },
|
||||
}
|
||||
|
||||
@@ -56,6 +56,7 @@ export const reverseEmailLookupTool: ToolConfig<
|
||||
success: false,
|
||||
error:
|
||||
(errorData as Record<string, string>).message ||
|
||||
(errorData as Record<string, string>).error ||
|
||||
`Findymail API error: ${response.status} ${response.statusText}`,
|
||||
output: {
|
||||
email: null,
|
||||
|
||||
@@ -50,6 +50,7 @@ export const searchTechnologiesTool: ToolConfig<
|
||||
success: false,
|
||||
error:
|
||||
(errorData as Record<string, string>).message ||
|
||||
(errorData as Record<string, string>).error ||
|
||||
`Findymail API error: ${response.status} ${response.statusText}`,
|
||||
output: { technologies: [] },
|
||||
}
|
||||
|
||||
@@ -44,6 +44,7 @@ export const verifyEmailTool: ToolConfig<FindymailVerifyEmailParams, FindymailVe
|
||||
success: false,
|
||||
error:
|
||||
(errorData as Record<string, string>).message ||
|
||||
(errorData as Record<string, string>).error ||
|
||||
`Findymail API error: ${response.status} ${response.statusText}`,
|
||||
output: { email: '', verified: false, provider: null },
|
||||
}
|
||||
|
||||
@@ -3176,6 +3176,14 @@ import {
|
||||
zoomListRecordingsTool,
|
||||
zoomUpdateMeetingTool,
|
||||
} from '@/tools/zoom'
|
||||
import {
|
||||
zoominfoEnrichCompaniesTool,
|
||||
zoominfoEnrichContactsTool,
|
||||
zoominfoSearchCompaniesTool,
|
||||
zoominfoSearchContactsTool,
|
||||
zoominfoSearchIntentTool,
|
||||
zoominfoSearchNewsTool,
|
||||
} from '@/tools/zoominfo'
|
||||
|
||||
// Registry of all available tools
|
||||
export const tools: Record<string, ToolConfig> = {
|
||||
@@ -4999,17 +5007,17 @@ export const tools: Record<string, ToolConfig> = {
|
||||
fathom_get_transcript: fathomGetTranscriptTool,
|
||||
fathom_list_team_members: fathomListTeamMembersTool,
|
||||
fathom_list_teams: fathomListTeamsTool,
|
||||
findymail_verify_email: findymailVerifyEmailTool,
|
||||
findymail_find_email_from_linkedin: findymailFindEmailFromLinkedInTool,
|
||||
findymail_find_email_from_name: findymailFindEmailFromNameTool,
|
||||
findymail_find_emails_by_domain: findymailFindEmailsByDomainTool,
|
||||
findymail_find_email_from_linkedin: findymailFindEmailFromLinkedInTool,
|
||||
findymail_reverse_email_lookup: findymailReverseEmailLookupTool,
|
||||
findymail_get_company: findymailGetCompanyTool,
|
||||
findymail_find_employees: findymailFindEmployeesTool,
|
||||
findymail_find_phone: findymailFindPhoneTool,
|
||||
findymail_search_technologies: findymailSearchTechnologiesTool,
|
||||
findymail_lookup_technologies: findymailLookupTechnologiesTool,
|
||||
findymail_get_company: findymailGetCompanyTool,
|
||||
findymail_get_credits: findymailGetCreditsTool,
|
||||
findymail_lookup_technologies: findymailLookupTechnologiesTool,
|
||||
findymail_reverse_email_lookup: findymailReverseEmailLookupTool,
|
||||
findymail_search_technologies: findymailSearchTechnologiesTool,
|
||||
findymail_verify_email: findymailVerifyEmailTool,
|
||||
stt_whisper: whisperSttTool,
|
||||
stt_whisper_v2: whisperSttV2Tool,
|
||||
stt_deepgram: deepgramSttTool,
|
||||
@@ -6006,6 +6014,12 @@ export const tools: Record<string, ToolConfig> = {
|
||||
zoom_get_meeting_recordings: zoomGetMeetingRecordingsTool,
|
||||
zoom_delete_recording: zoomDeleteRecordingTool,
|
||||
zoom_list_past_participants: zoomListPastParticipantsTool,
|
||||
zoominfo_search_companies: zoominfoSearchCompaniesTool,
|
||||
zoominfo_search_contacts: zoominfoSearchContactsTool,
|
||||
zoominfo_enrich_companies: zoominfoEnrichCompaniesTool,
|
||||
zoominfo_enrich_contacts: zoominfoEnrichContactsTool,
|
||||
zoominfo_search_intent: zoominfoSearchIntentTool,
|
||||
zoominfo_search_news: zoominfoSearchNewsTool,
|
||||
// Spotify
|
||||
spotify_search: spotifySearchTool,
|
||||
spotify_get_track: spotifyGetTrackTool,
|
||||
|
||||
@@ -137,6 +137,10 @@ export const wizaCompanyEnrichmentTool: ToolConfig<
|
||||
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 },
|
||||
credits: {
|
||||
type: 'json',
|
||||
description: 'Credits deducted for this enrichment (api_credits: { total, company_credits })',
|
||||
optional: true,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
@@ -100,9 +100,17 @@ export const wizaGetIndividualRevealTool: ToolConfig<
|
||||
},
|
||||
|
||||
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' },
|
||||
id: { type: 'number', description: 'Reveal ID', optional: true },
|
||||
status: {
|
||||
type: 'string',
|
||||
description: 'queued | resolving | finished | failed',
|
||||
optional: true,
|
||||
},
|
||||
is_complete: {
|
||||
type: 'boolean',
|
||||
description: 'Whether the reveal has completed',
|
||||
optional: true,
|
||||
},
|
||||
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 },
|
||||
@@ -158,6 +166,11 @@ export const wizaGetIndividualRevealTool: ToolConfig<
|
||||
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 },
|
||||
credits: {
|
||||
type: 'json',
|
||||
description:
|
||||
'Credits deducted for this reveal (api_credits: { total, email_credits, phone_credits, scrape_credits })',
|
||||
optional: true,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
@@ -132,11 +132,20 @@ export const wizaStartIndividualRevealTool: ToolConfig<
|
||||
},
|
||||
|
||||
outputs: {
|
||||
id: { type: 'number', description: 'Individual reveal ID (use with Get Individual Reveal)' },
|
||||
id: {
|
||||
type: 'number',
|
||||
description: 'Individual reveal ID (use with Get Individual Reveal)',
|
||||
optional: true,
|
||||
},
|
||||
status: {
|
||||
type: 'string',
|
||||
description: 'Reveal status: queued, resolving, finished, or failed',
|
||||
optional: true,
|
||||
},
|
||||
is_complete: {
|
||||
type: 'boolean',
|
||||
description: 'Whether the reveal has completed',
|
||||
optional: true,
|
||||
},
|
||||
is_complete: { type: 'boolean', description: 'Whether the reveal has completed' },
|
||||
},
|
||||
}
|
||||
|
||||
@@ -13,25 +13,36 @@ export interface WizaGetCreditsResponse extends ToolResponse {
|
||||
}
|
||||
}
|
||||
|
||||
export interface WizaIncludeExcludeFilter {
|
||||
v: string
|
||||
s: 'i' | 'e'
|
||||
}
|
||||
|
||||
export interface WizaLocationFilter {
|
||||
v: string | { country?: string; state?: string; city?: string }
|
||||
b: 'country' | 'state' | 'city'
|
||||
s: 'i' | 'e'
|
||||
}
|
||||
|
||||
export interface WizaProspectSearchParams {
|
||||
apiKey: string
|
||||
size?: number
|
||||
filters?: Record<string, unknown>
|
||||
first_name?: string[]
|
||||
last_name?: string[]
|
||||
job_title?: unknown[]
|
||||
job_title?: WizaIncludeExcludeFilter[]
|
||||
job_title_level?: string[]
|
||||
job_role?: string[]
|
||||
job_sub_role?: string[]
|
||||
location?: unknown[]
|
||||
location?: WizaLocationFilter[]
|
||||
skill?: string[]
|
||||
school?: string[]
|
||||
major?: string[]
|
||||
linkedin_slug?: string[]
|
||||
job_company?: unknown[]
|
||||
past_company?: unknown[]
|
||||
company_location?: unknown[]
|
||||
company_industry?: unknown[]
|
||||
job_company?: WizaIncludeExcludeFilter[]
|
||||
past_company?: WizaIncludeExcludeFilter[]
|
||||
company_location?: WizaLocationFilter[]
|
||||
company_industry?: WizaIncludeExcludeFilter[]
|
||||
company_size?: string[]
|
||||
company_type?: string[]
|
||||
}
|
||||
|
||||
@@ -0,0 +1,136 @@
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
import type {
|
||||
ZoomInfoEnrichCompaniesParams,
|
||||
ZoomInfoEnrichCompaniesResponse,
|
||||
} from '@/tools/zoominfo/types'
|
||||
import {
|
||||
buildProxyBody,
|
||||
extractDataArray,
|
||||
parseCsvOrJson,
|
||||
parseJsonField,
|
||||
transformZoomInfoEnvelope,
|
||||
ZOOMINFO_PROXY_URL,
|
||||
} from '@/tools/zoominfo/utils'
|
||||
|
||||
/**
|
||||
* Default output fields used when the caller does not specify any. ZoomInfo's
|
||||
* CompanyEnrich schema requires `outputFields`, so we send a useful firmographic
|
||||
* set rather than letting the request fail. All values are valid CompanyEnrich fields.
|
||||
*/
|
||||
const DEFAULT_COMPANY_OUTPUT_FIELDS = [
|
||||
'id',
|
||||
'name',
|
||||
'website',
|
||||
'domainList',
|
||||
'ticker',
|
||||
'revenue',
|
||||
'revenueRange',
|
||||
'employeeCount',
|
||||
'employeeRange',
|
||||
'primaryIndustry',
|
||||
'industries',
|
||||
'street',
|
||||
'city',
|
||||
'state',
|
||||
'zipCode',
|
||||
'country',
|
||||
'phone',
|
||||
'foundedYear',
|
||||
'companyStatus',
|
||||
'socialMediaUrls',
|
||||
'logo',
|
||||
'description',
|
||||
]
|
||||
|
||||
export const zoominfoEnrichCompaniesTool: ToolConfig<
|
||||
ZoomInfoEnrichCompaniesParams,
|
||||
ZoomInfoEnrichCompaniesResponse
|
||||
> = {
|
||||
id: 'zoominfo_enrich_companies',
|
||||
name: 'ZoomInfo Enrich Companies',
|
||||
description:
|
||||
'Enrich up to 25 companies in one request with detailed firmographics, industry, financials, and more.',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
clientId: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'ZoomInfo OAuth client ID',
|
||||
},
|
||||
clientSecret: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'ZoomInfo OAuth client secret',
|
||||
},
|
||||
matchCompanyInput: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description:
|
||||
'JSON array (1-25 items) of company matching criteria, e.g. [{"companyName":"Acme","companyWebsite":"acme.com"}]',
|
||||
},
|
||||
outputFields: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description:
|
||||
'JSON array or comma-separated list of fields to return (e.g. ["id","name","website","revenue","employeeCount"]). Defaults to a standard firmographic set if omitted.',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: ZOOMINFO_PROXY_URL,
|
||||
method: 'POST',
|
||||
headers: () => ({ 'Content-Type': 'application/json' }),
|
||||
body: (params) => {
|
||||
const matchCompanyInput = parseJsonField<unknown>(
|
||||
params.matchCompanyInput,
|
||||
'matchCompanyInput'
|
||||
)
|
||||
if (!Array.isArray(matchCompanyInput) || matchCompanyInput.length === 0) {
|
||||
throw new Error('matchCompanyInput must be a non-empty JSON array')
|
||||
}
|
||||
if (matchCompanyInput.length > 25) {
|
||||
throw new Error('matchCompanyInput supports a maximum of 25 entries per request')
|
||||
}
|
||||
|
||||
const outputFields = parseCsvOrJson(params.outputFields, 'outputFields')
|
||||
const attributes: Record<string, unknown> = {
|
||||
matchCompanyInput,
|
||||
outputFields: outputFields ?? DEFAULT_COMPANY_OUTPUT_FIELDS,
|
||||
}
|
||||
|
||||
return {
|
||||
...buildProxyBody(params),
|
||||
path: '/data/v1/companies/enrich',
|
||||
method: 'POST',
|
||||
body: {
|
||||
data: {
|
||||
type: 'CompanyEnrich',
|
||||
attributes,
|
||||
},
|
||||
},
|
||||
}
|
||||
},
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response) => {
|
||||
const { data } = await transformZoomInfoEnvelope(response)
|
||||
const results = extractDataArray(data)
|
||||
return {
|
||||
success: true,
|
||||
output: { results },
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
results: {
|
||||
type: 'array',
|
||||
description: 'Enrichment results, one per input with match status and attributes',
|
||||
items: { type: 'json' },
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,139 @@
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
import type {
|
||||
ZoomInfoEnrichContactsParams,
|
||||
ZoomInfoEnrichContactsResponse,
|
||||
} from '@/tools/zoominfo/types'
|
||||
import {
|
||||
buildProxyBody,
|
||||
extractDataArray,
|
||||
parseCsvOrJson,
|
||||
parseJsonField,
|
||||
transformZoomInfoEnvelope,
|
||||
ZOOMINFO_PROXY_URL,
|
||||
} from '@/tools/zoominfo/utils'
|
||||
|
||||
/**
|
||||
* Default output fields used when the caller does not specify any. ZoomInfo's
|
||||
* ContactEnrich schema requires `outputFields`, so we send a useful contact set
|
||||
* rather than letting the request fail. All values are valid ContactEnrich fields.
|
||||
*/
|
||||
const DEFAULT_CONTACT_OUTPUT_FIELDS = [
|
||||
'id',
|
||||
'firstName',
|
||||
'lastName',
|
||||
'email',
|
||||
'phone',
|
||||
'mobilePhone',
|
||||
'jobTitle',
|
||||
'jobFunction',
|
||||
'managementLevel',
|
||||
'city',
|
||||
'state',
|
||||
'country',
|
||||
'contactAccuracyScore',
|
||||
'validDate',
|
||||
'lastUpdatedDate',
|
||||
'companyId',
|
||||
'companyName',
|
||||
'companyWebsite',
|
||||
'companyPhone',
|
||||
]
|
||||
|
||||
export const zoominfoEnrichContactsTool: ToolConfig<
|
||||
ZoomInfoEnrichContactsParams,
|
||||
ZoomInfoEnrichContactsResponse
|
||||
> = {
|
||||
id: 'zoominfo_enrich_contacts',
|
||||
name: 'ZoomInfo Enrich Contacts',
|
||||
description:
|
||||
'Enrich up to 25 contacts in one request with verified emails, phone numbers, job details, and more.',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
clientId: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'ZoomInfo OAuth client ID',
|
||||
},
|
||||
clientSecret: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'ZoomInfo OAuth client secret',
|
||||
},
|
||||
matchPersonInput: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description:
|
||||
'JSON array (1-25 items) of contact matching criteria, e.g. [{"firstName":"Jane","lastName":"Doe","companyName":"Acme"}]',
|
||||
},
|
||||
outputFields: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description:
|
||||
'JSON array or comma-separated list of fields to return (e.g. ["id","firstName","email","phone","jobTitle"]). Defaults to a standard contact set if omitted.',
|
||||
},
|
||||
requiredFields: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description:
|
||||
'JSON array or comma-separated list of fields that must exist in results (e.g. ["email"])',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: ZOOMINFO_PROXY_URL,
|
||||
method: 'POST',
|
||||
headers: () => ({ 'Content-Type': 'application/json' }),
|
||||
body: (params) => {
|
||||
const matchPersonInput = parseJsonField<unknown>(params.matchPersonInput, 'matchPersonInput')
|
||||
if (!Array.isArray(matchPersonInput) || matchPersonInput.length === 0) {
|
||||
throw new Error('matchPersonInput must be a non-empty JSON array')
|
||||
}
|
||||
if (matchPersonInput.length > 25) {
|
||||
throw new Error('matchPersonInput supports a maximum of 25 entries per request')
|
||||
}
|
||||
|
||||
const outputFields = parseCsvOrJson(params.outputFields, 'outputFields')
|
||||
const attributes: Record<string, unknown> = {
|
||||
matchPersonInput,
|
||||
outputFields: outputFields ?? DEFAULT_CONTACT_OUTPUT_FIELDS,
|
||||
}
|
||||
const requiredFields = parseCsvOrJson(params.requiredFields, 'requiredFields')
|
||||
if (requiredFields) attributes.requiredFields = requiredFields
|
||||
|
||||
return {
|
||||
...buildProxyBody(params),
|
||||
path: '/data/v1/contacts/enrich',
|
||||
method: 'POST',
|
||||
body: {
|
||||
data: {
|
||||
type: 'ContactEnrich',
|
||||
attributes,
|
||||
},
|
||||
},
|
||||
}
|
||||
},
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response) => {
|
||||
const { data } = await transformZoomInfoEnvelope(response)
|
||||
const results = extractDataArray(data)
|
||||
return {
|
||||
success: true,
|
||||
output: { results },
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
results: {
|
||||
type: 'array',
|
||||
description: 'Enrichment results, one per input with match status and attributes',
|
||||
items: { type: 'json' },
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
export { zoominfoEnrichCompaniesTool } from '@/tools/zoominfo/enrich_companies'
|
||||
export { zoominfoEnrichContactsTool } from '@/tools/zoominfo/enrich_contacts'
|
||||
export { zoominfoSearchCompaniesTool } from '@/tools/zoominfo/search_companies'
|
||||
export { zoominfoSearchContactsTool } from '@/tools/zoominfo/search_contacts'
|
||||
export { zoominfoSearchIntentTool } from '@/tools/zoominfo/search_intent'
|
||||
export { zoominfoSearchNewsTool } from '@/tools/zoominfo/search_news'
|
||||
@@ -0,0 +1,213 @@
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
import type {
|
||||
ZoomInfoSearchCompaniesParams,
|
||||
ZoomInfoSearchCompaniesResponse,
|
||||
} from '@/tools/zoominfo/types'
|
||||
import {
|
||||
buildProxyBody,
|
||||
extractDataArray,
|
||||
extractPagination,
|
||||
paginationOutputProperties,
|
||||
parseCsvOrJson,
|
||||
toCsvStringOrUndefined,
|
||||
toNumberOrUndefined,
|
||||
transformZoomInfoEnvelope,
|
||||
ZOOMINFO_PROXY_URL,
|
||||
} from '@/tools/zoominfo/utils'
|
||||
|
||||
export const zoominfoSearchCompaniesTool: ToolConfig<
|
||||
ZoomInfoSearchCompaniesParams,
|
||||
ZoomInfoSearchCompaniesResponse
|
||||
> = {
|
||||
id: 'zoominfo_search_companies',
|
||||
name: 'ZoomInfo Search Companies',
|
||||
description: 'Search the ZoomInfo company database by name, industry, location, and size.',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
clientId: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'ZoomInfo OAuth client ID',
|
||||
},
|
||||
clientSecret: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'ZoomInfo OAuth client secret',
|
||||
},
|
||||
companyName: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Company name to search for',
|
||||
},
|
||||
companyWebsite: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Company website (comma-separated for multiple)',
|
||||
},
|
||||
companyTicker: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description:
|
||||
'Stock ticker symbols — JSON array, comma-separated list, or single ticker. Sent to the API as an array.',
|
||||
},
|
||||
industryCodes: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description:
|
||||
'Industry codes — JSON array or comma-separated list. Sent to the API as a comma-separated string.',
|
||||
},
|
||||
country: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Country name',
|
||||
},
|
||||
state: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'State or province',
|
||||
},
|
||||
metroRegion: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'US/Canada metro region',
|
||||
},
|
||||
revenueMin: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Minimum annual revenue in thousands USD',
|
||||
},
|
||||
revenueMax: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Maximum annual revenue in thousands USD',
|
||||
},
|
||||
employeeRangeMin: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Minimum employee count',
|
||||
},
|
||||
employeeRangeMax: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Maximum employee count',
|
||||
},
|
||||
excludeDefunctCompanies: {
|
||||
type: 'boolean',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Exclude inactive companies',
|
||||
},
|
||||
page: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Page number (1-based)',
|
||||
},
|
||||
rpp: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Results per page (1-100, default 25)',
|
||||
},
|
||||
sortBy: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Field to sort by',
|
||||
},
|
||||
sortOrder: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Sort order (asc or desc)',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: ZOOMINFO_PROXY_URL,
|
||||
method: 'POST',
|
||||
headers: () => ({ 'Content-Type': 'application/json' }),
|
||||
body: (params) => {
|
||||
const attributes: Record<string, unknown> = {}
|
||||
if (params.companyName) attributes.companyName = params.companyName
|
||||
if (params.companyWebsite) attributes.companyWebsite = params.companyWebsite
|
||||
const companyTicker = parseCsvOrJson(params.companyTicker, 'companyTicker')
|
||||
if (companyTicker) attributes.companyTicker = companyTicker
|
||||
const industryCodes = toCsvStringOrUndefined(params.industryCodes, 'industryCodes')
|
||||
if (industryCodes) attributes.industryCodes = industryCodes
|
||||
if (params.country) attributes.country = params.country
|
||||
if (params.state) attributes.state = params.state
|
||||
if (params.metroRegion) attributes.metroRegion = params.metroRegion
|
||||
const revenueMin = toNumberOrUndefined(params.revenueMin)
|
||||
if (revenueMin !== undefined) attributes.revenueMin = revenueMin
|
||||
const revenueMax = toNumberOrUndefined(params.revenueMax)
|
||||
if (revenueMax !== undefined) attributes.revenueMax = revenueMax
|
||||
const employeeRangeMin = toNumberOrUndefined(params.employeeRangeMin)
|
||||
if (employeeRangeMin !== undefined) attributes.employeeRangeMin = String(employeeRangeMin)
|
||||
const employeeRangeMax = toNumberOrUndefined(params.employeeRangeMax)
|
||||
if (employeeRangeMax !== undefined) attributes.employeeRangeMax = String(employeeRangeMax)
|
||||
if (params.excludeDefunctCompanies !== undefined) {
|
||||
attributes.excludeDefunctCompanies = params.excludeDefunctCompanies
|
||||
}
|
||||
|
||||
const query: Record<string, string | number> = {}
|
||||
const page = toNumberOrUndefined(params.page)
|
||||
const rpp = toNumberOrUndefined(params.rpp)
|
||||
if (page !== undefined) query['page[number]'] = page
|
||||
if (rpp !== undefined) query['page[size]'] = rpp
|
||||
if (params.sortBy) {
|
||||
const order = params.sortOrder === 'desc' ? '-' : ''
|
||||
query.sort = `${order}${params.sortBy}`
|
||||
}
|
||||
|
||||
return {
|
||||
...buildProxyBody(params),
|
||||
path: '/data/v1/companies/search',
|
||||
method: 'POST',
|
||||
query: Object.keys(query).length > 0 ? query : undefined,
|
||||
body: {
|
||||
data: {
|
||||
type: 'CompanySearch',
|
||||
attributes,
|
||||
},
|
||||
},
|
||||
}
|
||||
},
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response) => {
|
||||
const { data } = await transformZoomInfoEnvelope(response)
|
||||
const companies = extractDataArray(data)
|
||||
const pagination = extractPagination(data)
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
companies,
|
||||
...pagination,
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
companies: {
|
||||
type: 'array',
|
||||
description: 'Matching companies',
|
||||
items: { type: 'json' },
|
||||
},
|
||||
...paginationOutputProperties,
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,212 @@
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
import type {
|
||||
ZoomInfoSearchContactsParams,
|
||||
ZoomInfoSearchContactsResponse,
|
||||
} from '@/tools/zoominfo/types'
|
||||
import {
|
||||
buildProxyBody,
|
||||
extractDataArray,
|
||||
extractPagination,
|
||||
paginationOutputProperties,
|
||||
toCsvStringOrUndefined,
|
||||
toNumberOrUndefined,
|
||||
transformZoomInfoEnvelope,
|
||||
ZOOMINFO_PROXY_URL,
|
||||
} from '@/tools/zoominfo/utils'
|
||||
|
||||
export const zoominfoSearchContactsTool: ToolConfig<
|
||||
ZoomInfoSearchContactsParams,
|
||||
ZoomInfoSearchContactsResponse
|
||||
> = {
|
||||
id: 'zoominfo_search_contacts',
|
||||
name: 'ZoomInfo Search Contacts',
|
||||
description:
|
||||
'Search ZoomInfo for contacts (people) by name, job title, company, and other filters. Does not return emails or phone numbers — use Enrich Contacts for engagement data.',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
clientId: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'ZoomInfo OAuth client ID',
|
||||
},
|
||||
clientSecret: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'ZoomInfo OAuth client secret',
|
||||
},
|
||||
firstName: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'First name',
|
||||
},
|
||||
lastName: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Last name',
|
||||
},
|
||||
fullName: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Full name',
|
||||
},
|
||||
emailAddress: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Email address',
|
||||
},
|
||||
jobTitle: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Job title',
|
||||
},
|
||||
managementLevel: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description:
|
||||
'Management level — JSON array or comma-separated list. Sent to the API as a comma-separated string.',
|
||||
},
|
||||
department: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description:
|
||||
'Department — JSON array or comma-separated list. Sent to the API as a comma-separated string.',
|
||||
},
|
||||
companyId: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'ZoomInfo company ID',
|
||||
},
|
||||
companyName: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Company name',
|
||||
},
|
||||
contactAccuracyScoreMin: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Minimum accuracy score (70-99)',
|
||||
},
|
||||
requiredFields: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description:
|
||||
'Fields that must exist in results — JSON array or comma-separated list. Sent to the API as a comma-separated string.',
|
||||
},
|
||||
excludePartialProfiles: {
|
||||
type: 'boolean',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Exclude partial profiles',
|
||||
},
|
||||
page: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Page number (1-based)',
|
||||
},
|
||||
rpp: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Results per page (1-100, default 25)',
|
||||
},
|
||||
sortBy: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Field to sort by',
|
||||
},
|
||||
sortOrder: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Sort order (asc or desc)',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: ZOOMINFO_PROXY_URL,
|
||||
method: 'POST',
|
||||
headers: () => ({ 'Content-Type': 'application/json' }),
|
||||
body: (params) => {
|
||||
const attributes: Record<string, unknown> = {}
|
||||
if (params.firstName) attributes.firstName = params.firstName
|
||||
if (params.lastName) attributes.lastName = params.lastName
|
||||
if (params.fullName) attributes.fullName = params.fullName
|
||||
if (params.emailAddress) attributes.emailAddress = params.emailAddress
|
||||
if (params.jobTitle) attributes.jobTitle = params.jobTitle
|
||||
const managementLevel = toCsvStringOrUndefined(params.managementLevel, 'managementLevel')
|
||||
if (managementLevel) attributes.managementLevel = managementLevel
|
||||
const department = toCsvStringOrUndefined(params.department, 'department')
|
||||
if (department) attributes.department = department
|
||||
if (params.companyId) attributes.companyId = params.companyId
|
||||
if (params.companyName) attributes.companyName = params.companyName
|
||||
const minScore = toNumberOrUndefined(params.contactAccuracyScoreMin)
|
||||
if (minScore !== undefined) attributes.contactAccuracyScoreMin = String(minScore)
|
||||
const required = toCsvStringOrUndefined(params.requiredFields, 'requiredFields')
|
||||
if (required) attributes.requiredFields = required
|
||||
if (params.excludePartialProfiles !== undefined) {
|
||||
attributes.excludePartialProfiles = params.excludePartialProfiles
|
||||
}
|
||||
|
||||
const query: Record<string, string | number> = {}
|
||||
const page = toNumberOrUndefined(params.page)
|
||||
const rpp = toNumberOrUndefined(params.rpp)
|
||||
if (page !== undefined) query['page[number]'] = page
|
||||
if (rpp !== undefined) query['page[size]'] = rpp
|
||||
if (params.sortBy) {
|
||||
const order = params.sortOrder === 'desc' ? '-' : ''
|
||||
query.sort = `${order}${params.sortBy}`
|
||||
}
|
||||
|
||||
return {
|
||||
...buildProxyBody(params),
|
||||
path: '/data/v1/contacts/search',
|
||||
method: 'POST',
|
||||
query: Object.keys(query).length > 0 ? query : undefined,
|
||||
body: {
|
||||
data: {
|
||||
type: 'ContactSearch',
|
||||
attributes,
|
||||
},
|
||||
},
|
||||
}
|
||||
},
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response) => {
|
||||
const { data } = await transformZoomInfoEnvelope(response)
|
||||
const contacts = extractDataArray(data)
|
||||
const pagination = extractPagination(data)
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
contacts,
|
||||
...pagination,
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
contacts: {
|
||||
type: 'array',
|
||||
description: 'Matching contacts (without emails or phone numbers)',
|
||||
items: { type: 'json' },
|
||||
},
|
||||
...paginationOutputProperties,
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,190 @@
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
import type {
|
||||
ZoomInfoSearchIntentParams,
|
||||
ZoomInfoSearchIntentResponse,
|
||||
} from '@/tools/zoominfo/types'
|
||||
import {
|
||||
buildProxyBody,
|
||||
extractDataArray,
|
||||
extractPagination,
|
||||
paginationOutputProperties,
|
||||
parseCsvOrJson,
|
||||
toCsvStringOrUndefined,
|
||||
toNumberOrUndefined,
|
||||
transformZoomInfoEnvelope,
|
||||
ZOOMINFO_PROXY_URL,
|
||||
} from '@/tools/zoominfo/utils'
|
||||
|
||||
export const zoominfoSearchIntentTool: ToolConfig<
|
||||
ZoomInfoSearchIntentParams,
|
||||
ZoomInfoSearchIntentResponse
|
||||
> = {
|
||||
id: 'zoominfo_search_intent',
|
||||
name: 'ZoomInfo Search Intent',
|
||||
description: 'Search for companies showing intent signals on specific topics.',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
clientId: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'ZoomInfo OAuth client ID',
|
||||
},
|
||||
clientSecret: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'ZoomInfo OAuth client secret',
|
||||
},
|
||||
topics: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description:
|
||||
'Up to 50 intent topics as JSON array or comma-separated list (e.g. ["CRM Software","Marketing Automation"])',
|
||||
},
|
||||
signalStartDate: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Earliest signal date (YYYY-MM-DD)',
|
||||
},
|
||||
signalEndDate: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Latest signal date (YYYY-MM-DD)',
|
||||
},
|
||||
signalScoreMin: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Minimum signal score (60-100)',
|
||||
},
|
||||
signalScoreMax: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Maximum signal score (60-100)',
|
||||
},
|
||||
audienceStrengthMin: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Minimum audience strength (A-E, A is largest)',
|
||||
},
|
||||
audienceStrengthMax: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Maximum audience strength (A-E, A is largest)',
|
||||
},
|
||||
findRecommendedContacts: {
|
||||
type: 'boolean',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Include recommended contacts (default true)',
|
||||
},
|
||||
country: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Country filter',
|
||||
},
|
||||
state: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'State filter',
|
||||
},
|
||||
industryCodes: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description:
|
||||
'Industry codes — JSON array or comma-separated list. Sent to the API as a comma-separated string.',
|
||||
},
|
||||
page: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Page number (1-based)',
|
||||
},
|
||||
rpp: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Results per page (1-100, default 25)',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: ZOOMINFO_PROXY_URL,
|
||||
method: 'POST',
|
||||
headers: () => ({ 'Content-Type': 'application/json' }),
|
||||
body: (params) => {
|
||||
const topics = parseCsvOrJson(params.topics, 'topics')
|
||||
if (!topics || topics.length === 0) {
|
||||
throw new Error('topics is required')
|
||||
}
|
||||
const attributes: Record<string, unknown> = { topics }
|
||||
if (params.signalStartDate) attributes.signalStartDate = params.signalStartDate
|
||||
if (params.signalEndDate) attributes.signalEndDate = params.signalEndDate
|
||||
const scoreMin = toNumberOrUndefined(params.signalScoreMin)
|
||||
if (scoreMin !== undefined) attributes.signalScoreMin = scoreMin
|
||||
const scoreMax = toNumberOrUndefined(params.signalScoreMax)
|
||||
if (scoreMax !== undefined) attributes.signalScoreMax = scoreMax
|
||||
if (params.audienceStrengthMin) attributes.audienceStrengthMin = params.audienceStrengthMin
|
||||
if (params.audienceStrengthMax) attributes.audienceStrengthMax = params.audienceStrengthMax
|
||||
if (params.findRecommendedContacts !== undefined) {
|
||||
attributes.findRecommendedContacts = params.findRecommendedContacts
|
||||
}
|
||||
if (params.country) attributes.country = params.country
|
||||
if (params.state) attributes.state = params.state
|
||||
const industryCodes = toCsvStringOrUndefined(params.industryCodes, 'industryCodes')
|
||||
if (industryCodes) attributes.industryCodes = industryCodes
|
||||
|
||||
const query: Record<string, string | number> = {}
|
||||
const page = toNumberOrUndefined(params.page)
|
||||
const rpp = toNumberOrUndefined(params.rpp)
|
||||
if (page !== undefined) query['page[number]'] = page
|
||||
if (rpp !== undefined) query['page[size]'] = rpp
|
||||
|
||||
return {
|
||||
...buildProxyBody(params),
|
||||
path: '/data/v1/intent/search',
|
||||
method: 'POST',
|
||||
query: Object.keys(query).length > 0 ? query : undefined,
|
||||
body: {
|
||||
data: {
|
||||
type: 'IntentSearch',
|
||||
attributes,
|
||||
},
|
||||
},
|
||||
}
|
||||
},
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response) => {
|
||||
const { data } = await transformZoomInfoEnvelope(response)
|
||||
const signals = extractDataArray(data)
|
||||
const pagination = extractPagination(data)
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
signals,
|
||||
...pagination,
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
signals: {
|
||||
type: 'array',
|
||||
description: 'Intent signals with topic, score, audience strength, and company',
|
||||
items: { type: 'json' },
|
||||
},
|
||||
...paginationOutputProperties,
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,133 @@
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
import type { ZoomInfoSearchNewsParams, ZoomInfoSearchNewsResponse } from '@/tools/zoominfo/types'
|
||||
import {
|
||||
buildProxyBody,
|
||||
extractDataArray,
|
||||
extractPagination,
|
||||
paginationOutputProperties,
|
||||
parseCsvOrJson,
|
||||
toNumberOrUndefined,
|
||||
transformZoomInfoEnvelope,
|
||||
ZOOMINFO_PROXY_URL,
|
||||
} from '@/tools/zoominfo/utils'
|
||||
|
||||
export const zoominfoSearchNewsTool: ToolConfig<
|
||||
ZoomInfoSearchNewsParams,
|
||||
ZoomInfoSearchNewsResponse
|
||||
> = {
|
||||
id: 'zoominfo_search_news',
|
||||
name: 'ZoomInfo Search News',
|
||||
description: 'Search ZoomInfo news articles by category, URL, or date range.',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
clientId: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'ZoomInfo OAuth client ID',
|
||||
},
|
||||
clientSecret: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'ZoomInfo OAuth client secret',
|
||||
},
|
||||
categories: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'News categories as JSON array or comma-separated list',
|
||||
},
|
||||
url: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'News source URLs as JSON array or comma-separated list',
|
||||
},
|
||||
pageDateMin: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Earliest publish date (YYYY-MM-DD)',
|
||||
},
|
||||
pageDateMax: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Latest publish date (YYYY-MM-DD)',
|
||||
},
|
||||
page: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Page number (1-based)',
|
||||
},
|
||||
rpp: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Results per page (1-100, default 25)',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: ZOOMINFO_PROXY_URL,
|
||||
method: 'POST',
|
||||
headers: () => ({ 'Content-Type': 'application/json' }),
|
||||
body: (params) => {
|
||||
const attributes: Record<string, unknown> = {}
|
||||
const categories = parseCsvOrJson(params.categories, 'categories')
|
||||
if (categories) attributes.categories = categories
|
||||
const urls = parseCsvOrJson(params.url, 'url')
|
||||
if (urls) attributes.url = urls
|
||||
if (params.pageDateMin) attributes.pageDateMin = params.pageDateMin
|
||||
if (params.pageDateMax) attributes.pageDateMax = params.pageDateMax
|
||||
|
||||
if (Object.keys(attributes).length === 0) {
|
||||
throw new Error('Provide at least one of: categories, url, pageDateMin, pageDateMax')
|
||||
}
|
||||
|
||||
const query: Record<string, string | number> = {}
|
||||
const page = toNumberOrUndefined(params.page)
|
||||
const rpp = toNumberOrUndefined(params.rpp)
|
||||
if (page !== undefined) query['page[number]'] = page
|
||||
if (rpp !== undefined) query['page[size]'] = rpp
|
||||
|
||||
return {
|
||||
...buildProxyBody(params),
|
||||
path: '/data/v1/news/search',
|
||||
method: 'POST',
|
||||
query: Object.keys(query).length > 0 ? query : undefined,
|
||||
body: {
|
||||
data: {
|
||||
type: 'NewsSearch',
|
||||
attributes,
|
||||
},
|
||||
},
|
||||
}
|
||||
},
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response) => {
|
||||
const { data } = await transformZoomInfoEnvelope(response)
|
||||
const articles = extractDataArray(data)
|
||||
const pagination = extractPagination(data)
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
articles,
|
||||
...pagination,
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
articles: {
|
||||
type: 'array',
|
||||
description: 'News articles matching the filters',
|
||||
items: { type: 'json' },
|
||||
},
|
||||
...paginationOutputProperties,
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,136 @@
|
||||
import type { ToolResponse } from '@/tools/types'
|
||||
|
||||
export interface ZoomInfoBaseParams {
|
||||
clientId: string
|
||||
clientSecret: string
|
||||
}
|
||||
|
||||
export interface ZoomInfoSearchCompaniesParams extends ZoomInfoBaseParams {
|
||||
companyName?: string
|
||||
companyWebsite?: string
|
||||
companyTicker?: string
|
||||
industryCodes?: string
|
||||
country?: string
|
||||
state?: string
|
||||
metroRegion?: string
|
||||
revenueMin?: number
|
||||
revenueMax?: number
|
||||
employeeRangeMin?: number
|
||||
employeeRangeMax?: number
|
||||
excludeDefunctCompanies?: boolean
|
||||
page?: number
|
||||
rpp?: number
|
||||
sortBy?: string
|
||||
sortOrder?: string
|
||||
}
|
||||
|
||||
export interface ZoomInfoSearchContactsParams extends ZoomInfoBaseParams {
|
||||
firstName?: string
|
||||
lastName?: string
|
||||
fullName?: string
|
||||
emailAddress?: string
|
||||
jobTitle?: string
|
||||
managementLevel?: string
|
||||
department?: string
|
||||
companyId?: string
|
||||
companyName?: string
|
||||
contactAccuracyScoreMin?: number
|
||||
requiredFields?: string
|
||||
excludePartialProfiles?: boolean
|
||||
page?: number
|
||||
rpp?: number
|
||||
sortBy?: string
|
||||
sortOrder?: string
|
||||
}
|
||||
|
||||
export interface ZoomInfoEnrichCompaniesParams extends ZoomInfoBaseParams {
|
||||
matchCompanyInput: string
|
||||
outputFields?: string
|
||||
}
|
||||
|
||||
export interface ZoomInfoEnrichContactsParams extends ZoomInfoBaseParams {
|
||||
matchPersonInput: string
|
||||
outputFields?: string
|
||||
requiredFields?: string
|
||||
}
|
||||
|
||||
export interface ZoomInfoSearchIntentParams extends ZoomInfoBaseParams {
|
||||
topics: string
|
||||
signalStartDate?: string
|
||||
signalEndDate?: string
|
||||
signalScoreMin?: number
|
||||
signalScoreMax?: number
|
||||
audienceStrengthMin?: string
|
||||
audienceStrengthMax?: string
|
||||
findRecommendedContacts?: boolean
|
||||
country?: string
|
||||
state?: string
|
||||
industryCodes?: string
|
||||
page?: number
|
||||
rpp?: number
|
||||
}
|
||||
|
||||
export interface ZoomInfoSearchNewsParams extends ZoomInfoBaseParams {
|
||||
categories?: string
|
||||
url?: string
|
||||
pageDateMin?: string
|
||||
pageDateMax?: string
|
||||
page?: number
|
||||
rpp?: number
|
||||
}
|
||||
|
||||
export interface ZoomInfoSearchCompaniesResponse extends ToolResponse {
|
||||
output: {
|
||||
companies: Array<Record<string, unknown>>
|
||||
totalResults: number | null
|
||||
currentPage: number | null
|
||||
totalPages: number | null
|
||||
}
|
||||
}
|
||||
|
||||
export interface ZoomInfoSearchContactsResponse extends ToolResponse {
|
||||
output: {
|
||||
contacts: Array<Record<string, unknown>>
|
||||
totalResults: number | null
|
||||
currentPage: number | null
|
||||
totalPages: number | null
|
||||
}
|
||||
}
|
||||
|
||||
export interface ZoomInfoEnrichCompaniesResponse extends ToolResponse {
|
||||
output: {
|
||||
results: Array<Record<string, unknown>>
|
||||
}
|
||||
}
|
||||
|
||||
export interface ZoomInfoEnrichContactsResponse extends ToolResponse {
|
||||
output: {
|
||||
results: Array<Record<string, unknown>>
|
||||
}
|
||||
}
|
||||
|
||||
export interface ZoomInfoSearchIntentResponse extends ToolResponse {
|
||||
output: {
|
||||
signals: Array<Record<string, unknown>>
|
||||
totalResults: number | null
|
||||
currentPage: number | null
|
||||
totalPages: number | null
|
||||
}
|
||||
}
|
||||
|
||||
export interface ZoomInfoSearchNewsResponse extends ToolResponse {
|
||||
output: {
|
||||
articles: Array<Record<string, unknown>>
|
||||
totalResults: number | null
|
||||
currentPage: number | null
|
||||
totalPages: number | null
|
||||
}
|
||||
}
|
||||
|
||||
export type ZoomInfoResponse =
|
||||
| ZoomInfoSearchCompaniesResponse
|
||||
| ZoomInfoSearchContactsResponse
|
||||
| ZoomInfoEnrichCompaniesResponse
|
||||
| ZoomInfoEnrichContactsResponse
|
||||
| ZoomInfoSearchIntentResponse
|
||||
| ZoomInfoSearchNewsResponse
|
||||
@@ -0,0 +1,136 @@
|
||||
import { getErrorMessage } from '@sim/utils/errors'
|
||||
import type { OutputProperty } from '@/tools/types'
|
||||
import type { ZoomInfoBaseParams } from '@/tools/zoominfo/types'
|
||||
|
||||
export const ZOOMINFO_PROXY_URL = '/api/tools/zoominfo/proxy'
|
||||
|
||||
export interface ZoomInfoProxyEnvelope {
|
||||
clientId: string
|
||||
clientSecret: string
|
||||
path: string
|
||||
method: 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE'
|
||||
query?: Record<string, string | number | boolean>
|
||||
body?: unknown
|
||||
}
|
||||
|
||||
export function buildProxyBody(
|
||||
params: ZoomInfoBaseParams
|
||||
): Pick<ZoomInfoProxyEnvelope, 'clientId' | 'clientSecret'> {
|
||||
return {
|
||||
clientId: params.clientId,
|
||||
clientSecret: params.clientSecret,
|
||||
}
|
||||
}
|
||||
|
||||
export function parseJsonField<T>(value: unknown, fieldName: string): T {
|
||||
if (typeof value !== 'string') return value as T
|
||||
const trimmed = value.trim()
|
||||
if (!trimmed) {
|
||||
throw new Error(`${fieldName} is required`)
|
||||
}
|
||||
try {
|
||||
return JSON.parse(trimmed) as T
|
||||
} catch (error) {
|
||||
throw new Error(`${fieldName} must be valid JSON: ${getErrorMessage(error)}`)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Normalize a JSON-array string, real array, or comma-separated string into a
|
||||
* single comma-separated string. Use for ZoomInfo attributes that the docs
|
||||
* describe as a scalar string accepting a comma-separated list
|
||||
* (e.g. industryCodes, managementLevel, department).
|
||||
*/
|
||||
export function toCsvStringOrUndefined(value: unknown, fieldName: string): string | undefined {
|
||||
const arr = parseCsvOrJson(value, fieldName)
|
||||
if (!arr || arr.length === 0) return undefined
|
||||
return arr.join(',')
|
||||
}
|
||||
|
||||
export function parseCsvOrJson(value: unknown, fieldName: string): string[] | undefined {
|
||||
if (value === undefined || value === null) return undefined
|
||||
if (Array.isArray(value)) return value.map(String)
|
||||
if (typeof value !== 'string') return undefined
|
||||
const trimmed = value.trim()
|
||||
if (!trimmed) return undefined
|
||||
if (trimmed.startsWith('[')) {
|
||||
try {
|
||||
const parsed = JSON.parse(trimmed)
|
||||
if (!Array.isArray(parsed)) {
|
||||
throw new Error(`${fieldName} JSON must be an array of strings`)
|
||||
}
|
||||
return parsed.map(String)
|
||||
} catch (error) {
|
||||
throw new Error(`${fieldName} must be valid JSON: ${getErrorMessage(error)}`)
|
||||
}
|
||||
}
|
||||
return trimmed
|
||||
.split(',')
|
||||
.map((s) => s.trim())
|
||||
.filter(Boolean)
|
||||
}
|
||||
|
||||
export function toNumberOrUndefined(value: unknown): number | undefined {
|
||||
if (value === undefined || value === null || value === '') return undefined
|
||||
const n = Number(value)
|
||||
return Number.isFinite(n) ? n : undefined
|
||||
}
|
||||
|
||||
export async function transformZoomInfoEnvelope(
|
||||
response: Response
|
||||
): Promise<{ status: number; data: unknown }> {
|
||||
const data = (await response.json()) as
|
||||
| { success: true; output: { status: number; data: unknown } }
|
||||
| { success: false; error?: string; status?: number }
|
||||
if (!('success' in data) || data.success === false) {
|
||||
const errMessage = 'error' in data && data.error ? data.error : 'ZoomInfo request failed'
|
||||
throw new Error(errMessage)
|
||||
}
|
||||
return { status: data.output.status, data: data.output.data }
|
||||
}
|
||||
|
||||
export const paginationOutputProperties: Record<string, OutputProperty> = {
|
||||
totalResults: {
|
||||
type: 'number',
|
||||
description: 'Total number of matching results across all pages',
|
||||
optional: true,
|
||||
},
|
||||
currentPage: {
|
||||
type: 'number',
|
||||
description: 'Current page number',
|
||||
optional: true,
|
||||
},
|
||||
totalPages: {
|
||||
type: 'number',
|
||||
description: 'Total number of pages available',
|
||||
optional: true,
|
||||
},
|
||||
}
|
||||
|
||||
export function extractPagination(payload: unknown): {
|
||||
totalResults: number | null
|
||||
currentPage: number | null
|
||||
totalPages: number | null
|
||||
} {
|
||||
if (payload && typeof payload === 'object') {
|
||||
const meta = (payload as Record<string, unknown>).meta as
|
||||
| { totalResults?: unknown; page?: { number?: unknown; total?: unknown } }
|
||||
| undefined
|
||||
if (meta) {
|
||||
const totalResults = typeof meta.totalResults === 'number' ? meta.totalResults : null
|
||||
const currentPage =
|
||||
meta.page && typeof meta.page.number === 'number' ? meta.page.number : null
|
||||
const totalPages = meta.page && typeof meta.page.total === 'number' ? meta.page.total : null
|
||||
return { totalResults, currentPage, totalPages }
|
||||
}
|
||||
}
|
||||
return { totalResults: null, currentPage: null, totalPages: null }
|
||||
}
|
||||
|
||||
export function extractDataArray(payload: unknown): Array<Record<string, unknown>> {
|
||||
if (payload && typeof payload === 'object') {
|
||||
const data = (payload as Record<string, unknown>).data
|
||||
if (Array.isArray(data)) return data as Array<Record<string, unknown>>
|
||||
}
|
||||
return []
|
||||
}
|
||||
Reference in New Issue
Block a user