Skip to content

Commit 4a92779

Browse files
committed
fix(crunchbase): widen tier-gated collection allowlists and cap the deleted feed
The deleted-entity, autocomplete, and fields-metadata allowlists each held only the collections the narrowest package tier publishes, so requests valid on a richer package were rejected locally before any request went out. An Advanced Financials key could not read the funding-round deletion feed at all. - deleted-entity collections: 9 -> the 14-collection union across all tiers - autocomplete and fields-metadata: 14 -> all 43 collections - clamp the deleted feed to its documented max of 25, not Search's 1000 - offer "All collections" so the cross-collection feed stays reachable - name the richer-tier card additions instead of presenting the base set as exhaustive Also rewrites a test that asserted the broken behavior and tightens a substring URL assertion that passed on the value it was meant to reject.
1 parent 1ced0b6 commit 4a92779

9 files changed

Lines changed: 93 additions & 56 deletions

File tree

apps/docs/content/docs/en/integrations/crunchbase.mdx

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ Look up a single Crunchbase organization by permalink or UUID, returning the req
7979
| `apiKey` | string | Yes | Crunchbase API key, sent as the X-cb-user-key header |
8080
| `entityId` | string | Yes | Organization permalink \(e.g. "tesla-motors"\) or UUID |
8181
| `fieldIds` | json | No | Organization fields to return, e.g. \["identifier","name","founded_on","categories"\]. Defaults to identifier, name, short_description, website_url, linkedin, location_identifiers, categories, founded_on, num_employees_enum, operating_status, rank_org, permalink. |
82-
| `cardIds` | json | No | Related-entity cards to include, e.g. \["founders","headquarters_address"\]. Available on every license tier: child_organizations, child_ownerships, event_appearances, fields, founders, headquarters_address, parent_organization, parent_ownership. A card returns at most 100 items. |
82+
| `cardIds` | json | No | Related-entity cards to include, e.g. \["founders","headquarters_address"\]. Available on every license tier: child_organizations, child_ownerships, event_appearances, fields, founders, headquarters_address, parent_organization, parent_ownership. Richer packages add acquiree_acquisitions, acquirer_acquisitions, investors, ipos, jobs, participated_funding_rounds, participated_funds, participated_investments, press_references, raised_funding_rounds, raised_funds, raised_investments. A card returns at most 100 items. |
8383

8484
#### Output
8585

@@ -126,7 +126,7 @@ Look up a single Crunchbase person by permalink or UUID, returning the requested
126126
| `apiKey` | string | Yes | Crunchbase API key, sent as the X-cb-user-key header |
127127
| `entityId` | string | Yes | Person permalink \(e.g. "elon-musk"\) or UUID |
128128
| `fieldIds` | json | No | Person fields to return, e.g. \["identifier","name","primary_job_title","primary_organization"\]. Defaults to identifier, name, first_name, last_name, primary_job_title, primary_organization, short_description, location_identifiers, linkedin, rank_person, permalink. |
129-
| `cardIds` | json | No | Related-entity cards to include, e.g. \["jobs","primary_organization"\]. Available: degrees, event_appearances, fields, founded_organizations, jobs, primary_job, primary_organization. A card returns at most 100 items. |
129+
| `cardIds` | json | No | Related-entity cards to include, e.g. \["jobs","primary_organization"\]. Available on every license tier: degrees, event_appearances, fields, founded_organizations, jobs, primary_job, primary_organization. Advanced Financials adds participated_funding_rounds, participated_funds, participated_investments, partner_funding_rounds, partner_investments, press_references. A card returns at most 100 items. |
130130

131131
#### Output
132132

@@ -317,7 +317,7 @@ Suggest Crunchbase entities matching a typed query, returning the permalinks and
317317
| --------- | ---- | -------- | ----------- |
318318
| `apiKey` | string | Yes | Crunchbase API key, sent as the X-cb-user-key header |
319319
| `query` | string | Yes | Text to autocomplete against, e.g. "airbnb" |
320-
| `collectionIds` | json | No | Collections to search, e.g. \["organizations","people"\]. One or more of: addresses, categories, category_groups, degrees, diversity_spotlights, event_appearances, events, ipos, jobs, locations, organizations, ownerships, people, principals. Defaults to every collection. |
320+
| `collectionIds` | json | No | Collections to search, e.g. \["organizations","people"\]. One or more of: acquisition_predictions, acquisitions, addresses, awards, categories, category_groups, closure_predictions, current_valuation_estimates, degrees, diversity_spotlights, event_appearances, events, funding_predictions, funding_rounds, funds, growth_insights, growth_predictions, investments, investor_insights, investor_matches, ipo_predictions, ipos, jobs, key_employee_changes, layoff_predictions, layoffs, legal_proceedings, locations, market_insight_reasons, market_insights, micro_categories, org_similarities, organizations, ownerships, partnership_announcements, people, press_references, principals, product_launches, product_similarities, products, remain_private_predictions, research_insights. Which of them your key can read depends on your Crunchbase package. Defaults to every collection. |
321321
| `limit` | number | No | Suggestions to return, max 25 \(default 10\) |
322322

323323
#### Output
@@ -335,10 +335,10 @@ List entities Crunchbase has deleted, so a mirrored copy can be pruned in step w
335335
| Parameter | Type | Required | Description |
336336
| --------- | ---- | -------- | ----------- |
337337
| `apiKey` | string | Yes | Crunchbase API key, sent as the X-cb-user-key header |
338-
| `collection` | string | No | Restrict the feed to a single collection: categories, event_appearances, events, ipos, jobs, locations, organizations, ownerships, or people. Leave empty to read the feed across collections. |
339-
| `collectionIds` | json | No | Collections to include when reading the cross-collection feed, e.g. \["organizations","people"\]. Ignored when a single collection is set. |
338+
| `collection` | string | No | Restrict the feed to a single collection: acquisitions, categories, event_appearances, events, funding_rounds, funds, investments, ipos, jobs, locations, organizations, ownerships, people, or press_references. Which of them your key can read depends on your Crunchbase package. Leave empty to read the feed across collections. |
339+
| `collectionIds` | json | No | Collections to include when reading the cross-collection feed, e.g. \["organizations","people"\]. One or more of: acquisitions, categories, event_appearances, events, funding_rounds, funds, investments, ipos, jobs, locations, organizations, ownerships, people, press_references. Ignored when a single collection is set. |
340340
| `deletedAtOrder` | string | No | Order by deletion time: "asc" \(default\) or "desc" |
341-
| `limit` | number | No | Rows to return per page |
341+
| `limit` | number | No | Rows to return per page, 1-25 \(default 10\) |
342342
| `afterId` | string | No | UUID of the last row on the current page, to fetch the next page |
343343
| `beforeId` | string | No | UUID of the first row on the current page, to fetch the previous page |
344344

@@ -358,7 +358,7 @@ List the field ids, types, and descriptions each Crunchbase collection publishes
358358
| Parameter | Type | Required | Description |
359359
| --------- | ---- | -------- | ----------- |
360360
| `apiKey` | string | Yes | Crunchbase API key, sent as the X-cb-user-key header |
361-
| `collectionIds` | json | No | Collections to describe, e.g. \["organizations","people"\]. One or more of: addresses, categories, category_groups, degrees, diversity_spotlights, event_appearances, events, ipos, jobs, locations, organizations, ownerships, people, principals. Defaults to every collection. |
361+
| `collectionIds` | json | No | Collections to describe, e.g. \["organizations","people"\]. One or more of: acquisition_predictions, acquisitions, addresses, awards, categories, category_groups, closure_predictions, current_valuation_estimates, degrees, diversity_spotlights, event_appearances, events, funding_predictions, funding_rounds, funds, growth_insights, growth_predictions, investments, investor_insights, investor_matches, ipo_predictions, ipos, jobs, key_employee_changes, layoff_predictions, layoffs, legal_proceedings, locations, market_insight_reasons, market_insights, micro_categories, org_similarities, organizations, ownerships, partnership_announcements, people, press_references, principals, product_launches, product_similarities, products, remain_private_predictions, research_insights. Which of them your key can read depends on your Crunchbase package. Defaults to every collection. |
362362

363363
#### Output
364364

apps/sim/blocks/blocks/crunchbase.ts

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,14 @@ function toCollectionOption(id: string) {
5959

6060
const COLLECTION_OPTIONS = CRUNCHBASE_COLLECTIONS.map(toCollectionOption)
6161
const CARD_COLLECTION_OPTIONS = CRUNCHBASE_CARD_COLLECTIONS.map(toCollectionOption)
62-
const DELETED_COLLECTION_OPTIONS = CRUNCHBASE_DELETED_COLLECTIONS.map(toCollectionOption)
62+
/**
63+
* The empty id is what returns the picker to the cross-collection feed — without
64+
* it a chosen collection can be swapped but never cleared.
65+
*/
66+
const DELETED_COLLECTION_OPTIONS = [
67+
{ label: 'All collections', id: '' },
68+
...CRUNCHBASE_DELETED_COLLECTIONS.map(toCollectionOption),
69+
]
6370

6471
const QUERY_WAND_PROMPT = `Generate a Crunchbase Search API "query" array.
6572
@@ -303,7 +310,8 @@ export const CrunchbaseBlock: BlockConfig<CrunchbaseResponse> = {
303310
title: 'Limit',
304311
type: 'short-input',
305312
mode: 'advanced',
306-
placeholder: 'Search: 1-1000 (default 100). Card: 1-100. Autocomplete: 1-25 (default 10)',
313+
placeholder:
314+
'Search: 1-1000 (default 100). Card: 1-100. Autocomplete and deleted feed: 1-25 (default 10)',
307315
condition: { field: 'operation', value: LIMITED_OPERATIONS },
308316
},
309317
{

apps/sim/tools/crunchbase/autocomplete.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ export const crunchbaseAutocompleteTool: ToolConfig<
4747
required: false,
4848
visibility: 'user-or-llm',
4949
description:
50-
'Collections to search, e.g. ["organizations","people"]. One or more of: addresses, categories, category_groups, degrees, diversity_spotlights, event_appearances, events, ipos, jobs, locations, organizations, ownerships, people, principals. Defaults to every collection.',
50+
'Collections to search, e.g. ["organizations","people"]. One or more of: acquisition_predictions, acquisitions, addresses, awards, categories, category_groups, closure_predictions, current_valuation_estimates, degrees, diversity_spotlights, event_appearances, events, funding_predictions, funding_rounds, funds, growth_insights, growth_predictions, investments, investor_insights, investor_matches, ipo_predictions, ipos, jobs, key_employee_changes, layoff_predictions, layoffs, legal_proceedings, locations, market_insight_reasons, market_insights, micro_categories, org_similarities, organizations, ownerships, partnership_announcements, people, press_references, principals, product_launches, product_similarities, products, remain_private_predictions, research_insights. Which of them your key can read depends on your Crunchbase package. Defaults to every collection.',
5151
},
5252
limit: {
5353
type: 'number',

apps/sim/tools/crunchbase/crunchbase.test.ts

Lines changed: 34 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -167,7 +167,9 @@ describe('crunchbase request building', () => {
167167
cardId: 'participated_investments',
168168
cardFieldIds: '["uuid","announced_on"]',
169169
})
170-
expect(alreadyPresent).toContain('card_field_ids=uuid%2Cannounced_on')
170+
/* Substring-matching the prefix would also pass on a list that appended
171+
"identifier" anyway — only the exact value proves it was left alone. */
172+
expect(new URL(alreadyPresent).searchParams.get('card_field_ids')).toBe('uuid,announced_on')
171173
})
172174

173175
it('rejects both cursors on every paged endpoint, not just search', () => {
@@ -203,16 +205,45 @@ describe('crunchbase request building', () => {
203205
)
204206
})
205207

206-
it('rejects a collection the constrained endpoints do not accept', () => {
208+
it('rejects a collection no Crunchbase package publishes', () => {
207209
expect(() =>
208210
buildUrl(crunchbaseAutocompleteTool, {
209211
apiKey: 'k',
210212
query: 'airbnb',
211-
collectionIds: ['funds'],
213+
collectionIds: ['unicorns'],
212214
})
213215
).toThrow(/unsupported collection/)
214216
})
215217

218+
it('accepts a collection only a richer package publishes', () => {
219+
/* The allowlist is the union across packages, not the narrowest one — the
220+
Firmographic enum omits these, so a tier-narrow list would reject a
221+
request an Advanced Financials key answers. */
222+
expect(
223+
new URL(
224+
buildUrl(crunchbaseAutocompleteTool, {
225+
apiKey: 'k',
226+
query: 'sequoia',
227+
collectionIds: ['funds', 'funding_rounds', 'acquisitions'],
228+
})
229+
).searchParams.get('collection_ids')
230+
).toBe('funds,funding_rounds,acquisitions')
231+
232+
expect(
233+
buildUrl(crunchbaseListDeletedEntitiesTool, { apiKey: 'k', collection: 'funding_rounds' })
234+
).toBe('https://api.crunchbase.com/v4/data/deleted_entities/funding_rounds')
235+
})
236+
237+
it('caps the deleted feed at its own documented maximum, not the search maximum', () => {
238+
/* `/data/deleted_entities` documents "default = 10, max = 25" while Search
239+
allows 1000, and the block shares one Limit subblock across both. */
240+
expect(
241+
new URL(
242+
buildUrl(crunchbaseListDeletedEntitiesTool, { apiKey: 'k', limit: 1000 })
243+
).searchParams.get('limit')
244+
).toBe('25')
245+
})
246+
216247
it('reads fields metadata from the /md path, not /data', () => {
217248
expect(
218249
buildUrl(crunchbaseGetFieldsMetadataTool, { apiKey: 'k', collectionIds: ['organizations'] })

apps/sim/tools/crunchbase/get_fields_metadata.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ export const crunchbaseGetFieldsMetadataTool: ToolConfig<
4343
required: false,
4444
visibility: 'user-or-llm',
4545
description:
46-
'Collections to describe, e.g. ["organizations","people"]. One or more of: addresses, categories, category_groups, degrees, diversity_spotlights, event_appearances, events, ipos, jobs, locations, organizations, ownerships, people, principals. Defaults to every collection.',
46+
'Collections to describe, e.g. ["organizations","people"]. One or more of: acquisition_predictions, acquisitions, addresses, awards, categories, category_groups, closure_predictions, current_valuation_estimates, degrees, diversity_spotlights, event_appearances, events, funding_predictions, funding_rounds, funds, growth_insights, growth_predictions, investments, investor_insights, investor_matches, ipo_predictions, ipos, jobs, key_employee_changes, layoff_predictions, layoffs, legal_proceedings, locations, market_insight_reasons, market_insights, micro_categories, org_similarities, organizations, ownerships, partnership_announcements, people, press_references, principals, product_launches, product_similarities, products, remain_private_predictions, research_insights. Which of them your key can read depends on your Crunchbase package. Defaults to every collection.',
4747
},
4848
},
4949

apps/sim/tools/crunchbase/get_organization.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ export const crunchbaseGetOrganizationTool: ToolConfig<
4444
required: false,
4545
visibility: 'user-or-llm',
4646
description:
47-
'Related-entity cards to include, e.g. ["founders","headquarters_address"]. Available on every license tier: child_organizations, child_ownerships, event_appearances, fields, founders, headquarters_address, parent_organization, parent_ownership. A card returns at most 100 items.',
47+
'Related-entity cards to include, e.g. ["founders","headquarters_address"]. Available on every license tier: child_organizations, child_ownerships, event_appearances, fields, founders, headquarters_address, parent_organization, parent_ownership. Richer packages add acquiree_acquisitions, acquirer_acquisitions, investors, ipos, jobs, participated_funding_rounds, participated_funds, participated_investments, press_references, raised_funding_rounds, raised_funds, raised_investments. A card returns at most 100 items.',
4848
},
4949
},
5050

apps/sim/tools/crunchbase/get_person.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ export const crunchbaseGetPersonTool: ToolConfig<CrunchbaseEntityParams, Crunchb
4242
required: false,
4343
visibility: 'user-or-llm',
4444
description:
45-
'Related-entity cards to include, e.g. ["jobs","primary_organization"]. Available: degrees, event_appearances, fields, founded_organizations, jobs, primary_job, primary_organization. A card returns at most 100 items.',
45+
'Related-entity cards to include, e.g. ["jobs","primary_organization"]. Available on every license tier: degrees, event_appearances, fields, founded_organizations, jobs, primary_job, primary_organization. Advanced Financials adds participated_funding_rounds, participated_funds, participated_investments, partner_funding_rounds, partner_investments, press_references. A card returns at most 100 items.',
4646
},
4747
},
4848

apps/sim/tools/crunchbase/list_deleted_entities.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,9 @@ import {
99
clampLimit,
1010
crunchbaseError,
1111
crunchbaseHeaders,
12+
DELETED_LIMIT_MAX,
1213
parseIdListParam,
1314
readJson,
14-
SEARCH_LIMIT_MAX,
1515
} from '@/tools/crunchbase/utils'
1616
import { ErrorExtractorId } from '@/tools/error-extractors'
1717
import type { ToolConfig, ToolResponse } from '@/tools/types'
@@ -61,14 +61,14 @@ export const crunchbaseListDeletedEntitiesTool: ToolConfig<
6161
required: false,
6262
visibility: 'user-or-llm',
6363
description:
64-
'Restrict the feed to a single collection: categories, event_appearances, events, ipos, jobs, locations, organizations, ownerships, or people. Leave empty to read the feed across collections.',
64+
'Restrict the feed to a single collection: acquisitions, categories, event_appearances, events, funding_rounds, funds, investments, ipos, jobs, locations, organizations, ownerships, people, or press_references. Which of them your key can read depends on your Crunchbase package. Leave empty to read the feed across collections.',
6565
},
6666
collectionIds: {
6767
type: 'json',
6868
required: false,
6969
visibility: 'user-or-llm',
7070
description:
71-
'Collections to include when reading the cross-collection feed, e.g. ["organizations","people"]. Ignored when a single collection is set.',
71+
'Collections to include when reading the cross-collection feed, e.g. ["organizations","people"]. One or more of: acquisitions, categories, event_appearances, events, funding_rounds, funds, investments, ipos, jobs, locations, organizations, ownerships, people, press_references. Ignored when a single collection is set.',
7272
},
7373
deletedAtOrder: {
7474
type: 'string',
@@ -80,7 +80,7 @@ export const crunchbaseListDeletedEntitiesTool: ToolConfig<
8080
type: 'number',
8181
required: false,
8282
visibility: 'user-or-llm',
83-
description: 'Rows to return per page',
83+
description: 'Rows to return per page, 1-25 (default 10)',
8484
},
8585
afterId: {
8686
type: 'string',
@@ -117,7 +117,7 @@ export const crunchbaseListDeletedEntitiesTool: ToolConfig<
117117
if (params.deletedAtOrder === 'desc' || params.deletedAtOrder === 'asc') {
118118
search.set('deleted_at_order', params.deletedAtOrder)
119119
}
120-
const limit = clampLimit(params.limit, SEARCH_LIMIT_MAX)
120+
const limit = clampLimit(params.limit, DELETED_LIMIT_MAX)
121121
if (limit !== undefined) search.set('limit', String(limit))
122122
if (params.afterId) search.set('after_id', params.afterId)
123123
if (params.beforeId) search.set('before_id', params.beforeId)

0 commit comments

Comments
 (0)