Affinity
Bring your Affinity data into ChatGPT to search contacts, companies, and deals; prep for meetings using your actual notes and interaction history; and update records as deals progress. Every email captured, every meeting logged, every relationship scored by your firm is now available directly in the conversation.
- Integration type
- Plugin
- Verification status
- Not applicable
- Platform
- ChatGPT
- Primary Subcategory
- Investor & Deal-Flow Relationship CRM
- Secondary Subcategories
- None listed
- Brand
- Affinity
- Access
- Account required
- First tracked
- 2026-05-22
- Tool count
- 60
- Geography
- US
The Primary Subcategory used for this profile’s headline score.
Other Subcategories where the Integration is visible.
ChatGPT Plugin Discoverability Score
ChatGPT organic discovery is not live yet
Affinity is tracked in the ChatGPT Plugin registry. Public organic-discovery measurement is not live for ChatGPT yet, so there is no score to publish today.
Get notified when your score goes live
Enter your work email and we’ll notify you when ChatGPT Plugin organic discovery scoring launches.
No spam. Unsubscribe any time.
Competing in ChatGPT Investor & Deal-Flow Relationship CRM
View CategoryHow the Discoverability Score works
Organic discovery scoring for Affinity on ChatGPT is not live yet. The score will use measured agent conversations when it launches.
Organic discovery scoring is pending. Your Plugin score will appear on this scale when measurement goes live.
FoundDiagnostic
Whether Claude found your Plugin in connector search. It must be Found before it can reach the picker, but the score counts picker appearances—not search results.
PickedMain score
How often your Plugin appeared in the picker, or Claude invoked it directly, across contested conversations. This percentage is the Discoverability Score; the headline number is rounded.
PositionedDiagnostic
What position your Plugin appeared in when it was shown in the picker. This shows prominence, but it does not affect the score.
60 tools agents can invoke
Use when a user wants to import, migrate, or load data into Affinity — companies, people, or opportunities — from any source: another CRM, a CSV or spreadsheet export. Also use when the user wants to re-import records that failed a previous import, or clean and prepare a file for Affinity's CSV importer. Do NOT use for: LinkedIn connections import (Settings → LinkedIn Data), email/meeting interaction history (comes from email sync), or restoring deleted data.
Set up a complete event tracking workflow in Affinity CRM for private capital teams. Use this skill whenever a user wants to track an event in Affinity — including creating global fields for attendance, building lists for attendees (companies and/or people), adding list-level fields for event-specific data, sourcing attendees from CRM relationships, reviewing historical event data, drafting event communications, or processing post-event attendance from a CSV or manual list. Also use when the user mentions "event list", "conference tracking", "attendee list", "event setup in Affinity", "post-event follow-up", or "update attendance status". This skill runs an interactive, end-to-end workflow directly against the Affinity MCP.
Produce a relationship-intelligence pre-meeting brief from Affinity CRM data via the Affinity MCP. Use whenever the user asks to "prep me for", "prepare for", "brief me on", "get me ready for", or "tell me about" an upcoming meeting, call, person, or company, or asks what they should know before a meeting. Always runs a full multi-tool Affinity sequence and synthesizes it, never a single lookup. Read-only: never writes to the CRM.
Create a new company in Affinity. IMPORTANT: Call `search_companies_top_matches` first with the company's name or domain to avoid creating duplicates. Affinity does not deduplicate on create. Args: name: The company's name. Must be at least 1 character. Example: "Acme Corporation" domain: The company's primary domain, hostname only, no protocol or path. Example: "acme.com" person_ids: IDs of persons to associate with the company at creation time. Use `search_persons` to look up person IDs first. These associations may be applied asynchronously by the API. Example: [1234, 5678] Returns: The newly created Company object including id, name, domain, domains, and global fields. If `person_ids` were provided, the returned company may still show an empty or stale `persons` list immediately after creation because person associations are assigned asynchronously. Fetch the company again later if you need to verify the associated persons.
Create a new field (custom column) on a list or globally on an entity. IMPORTANT: - This tool does NOT create dropdown options. When value_type is DROPDOWN, the field is created with no options. Dropdown options are managed separately: for a list-specific field (list_id provided), use `create_list_field_dropdown_option` to add options. For a global field (list_id omitted), options must be managed via the Affinity UI/API. - Omitting `list_id` creates a global field on every entity of `entity_type`. This requires org-level "manage global fields" permission and will fail without it. Prefer passing a `list_id` unless the user explicitly wants a global field. Args: name: The field name. Example: "Relationship Strength" value_type: The kind of value the field holds. Options: 0 = person, 1 = company, 2 = text, 3 = number, 4 = date, 5 = location, 7 = dropdown. TEXT behaves as a single-line text field. entity_type: The entity the field applies to. One of EntityType.COMPANY, EntityType.PERSON, or EntityType.OPPORTUNITY. For a list-specific field this must match the list's entity type. list_id: ID of the list to attach the field to. When provided, the field is created as list-specific. When omitted, a global field is created (see IMPORTANT). Use `get_lists` to look up list IDs. allows_multiple: Whether the field accepts multiple values. Only valid for person, company, text, and location fields. Defaults to False. is_required: Whether the field is required. Only meaningful for list-specific fields. Setting this to True requires list-admin permission; it is only sent when True so that non-admin editors can still create optional fields. Defaults to False. Returns: The newly created field, including id, name, list_id, value_type, allows_multiple, and an (empty) dropdown_options list.
Log an interaction (meeting, call, or chat message) with one or more people. Args: interaction_type: The kind of interaction to log: - InteractionType.MEETING (0) - InteractionType.CALL (1) - InteractionType.CHAT_MESSAGE (2) If unclear which type the user means, ask before calling. date: When the interaction occurred, in ISO 8601 format. Example: "2025-01-31T10:56:29Z" person_ids: IDs of the persons involved in the interaction. At least one is required. Use search_persons to look up person IDs by name or email. content: Notes describing the interaction. direction: Direction of the message. Required when interaction_type is CHAT_MESSAGE; not applicable to meetings and calls (omit it for those). - ChatMessageDirection.SENT (0): message sent to the contact - ChatMessageDirection.RECEIVED (1): message received from the contact Returns: dict[str, Any]: API response containing the created interaction.
Create a new list for companies, people, or opportunities. IMPORTANT: The entity type is fixed when the list is created and cannot be changed later. A company list holds companies, a person list holds people, and an opportunity list holds opportunities. IMPORTANT: Setting is_public=True requires the "Share accessible Lists globally" permission. Without it, the request fails with a 403. Default to a private list (is_public=False) unless the user explicitly asks for a public/shared list. This tool only creates the empty list. To add entities, call create_list_entry (companies/people) or create_opportunity (opportunities) afterward. Args: name (required): Name of the list (1 to 255 characters). entity_type (required): Entity type the list holds. One of EntityType.COMPANY, EntityType.PERSON, or EntityType.OPPORTUNITY. is_public: Whether the list is visible to everyone in the organization. Defaults to False (private to the creator). Returns: JSON object with the new list's id, name, type, isPublic, creatorId, and ownerId.
Adds an existing company or person as a list entry on the specified list. Opportunities CANNOT be added using this tool. Args: list_id: ID of the list where the entry is being added. entity_id: ID of the existing person or company to add to this list. Returns: JSON object containing information about the newly created list entry.
Create a new dropdown option on a list's dropdown field. Adds a selectable value to a dropdown or ranked-dropdown column on a list, e.g. a new "Not Relevant" option on a Communication Status field. IMPORTANT: - option_type must match the field's actual type. Call `get_list_fields` to check the field's valueType before calling. A mismatch returns a 400. - This does not work on status fields, which Affinity manages separately; the API returns a 400 for them. - For a ranked-dropdown field, both rank and color are required. For a plain dropdown field, neither may be set. Args: list_id: ID of the list the field belongs to. Use `get_lists` to look it up. field_id: Human-readable field ID, e.g. "field-1234". Get it from `get_list_fields`. option_type: The field's dropdown type. One of FieldValueType.DROPDOWN or FieldValueType.RANKED_DROPDOWN. text: The option label (1 to 255 characters). Must be unique on the field. rank: Sort position for ranked-dropdown options (0 to 2147483647). Required for ranked-dropdown, omit for plain dropdown. color: Tag color for ranked-dropdown options. Options: 0 = white, 1 = gray, 2 = blue, 3 = green, 4 = purple, 5 = orange, 6 = red. Required for ranked-dropdown, omit for plain dropdown. Returns: The created option with id, type, text, and (for ranked-dropdown) rank and color.
Create a note attached to entities or a meeting, or as a reply to an existing note. IMPORTANT: When picking `meeting_id` from get_meetings_for_entity, ALWAYS use the response's `v2_id` field if it is present and non-null. Only fall back to the `id` field when `v2_id` is absent. Meetings, calls, and chat messages each allow at most one root note. This tool can create that root note only for a meeting, using meeting_id. A meeting synced from a calendar may not have one yet, so creating a meeting note when one already exists fails with an error that names the existing note's id. Calls and chat messages are created with their note, so they always already have a root note. To add to a call or chat message, or to a meeting that already has a note, reply instead: set note_type=USER_REPLY and parent_note_id to that note's id, and tell the user you are replying. parent_note_id accepts the root note of a meeting, call, or chat message. To find the note id: - create_interaction and get_meetings_for_entity return it in their response, so reply directly. - get_meetings does not return it, so try creating the meeting note first. If it fails with the root-note error, retry as a USER_REPLY using the id from the error. Args: note_type: Which kind of note to create. Options: - NoteType.ENTITIES (0): note tied to persons, companies, or opportunities. Requires at least one of person_ids, company_ids, opportunity_ids. - NoteType.INTERACTION (1): note tied to a meeting. Requires meeting_id. Entity IDs may be supplied for additional associations. - NoteType.USER_REPLY (2): reply to an existing note. Requires parent_note_id. Cannot be combined with entity IDs or interaction params. If unclear which type the user wants, ask before calling. content: HTML content of the note. Allowed tags (with no attributes other than those explicitly noted): <p>, <br>, <strong>, <em>, <u>, <ol>, <ul>, <li>, <span> (no attributes), <a> (only href, with http, https, or mailto URL schemes). Disallowed (any of these will cause the request to fail): inline style attributes, class attributes, <img>, <script>, <iframe>, <style>, <blockquote>, <hr>, <s>, <pre>, <code>, <font>. Mentions: mention spans (<span data-type="note-mention" ...>) are also disallowed. Mentions cannot be created or modified through this endpoint. Anchor normalization: the server appends rel="noopener noreferrer" and target="_blank" to every <a> before the note is saved, so the saved HTML will contain those attributes even though they were not sent. person_ids: Person IDs to attach the note to. Max 100. Used by ENTITIES and INTERACTION. company_ids: Company IDs to attach the note to. Max 100. Used by ENTITIES and INTERACTION. opportunity_ids: Opportunity IDs to attach the note to. Max 100. Used by ENTITIES and INTERACTION. meeting_id: ID of the meeting. Only required when note_type=INTERACTION. See the IMPORTANT note above for which id to use when sourcing this from a V1 response. parent_note_id: ID of the parent note to reply to. Only required when note_type=USER_REPLY. May be the root note of a meeting, call, or chat message. Returns: The created note containing id, type, content, creator, mentions, createdAt, updatedAt, plus additional fields depending on note type, and note_url pointing to the note in the Affinity CRM.
Creates a new Opportunity on the specified list. The list must be an opportunity-type list. Companies and/or persons can optionally be associated with the new Opportunity at creation time. The list entry on the target list is auto-created, no separate `create_list_entry` call is needed. IMPORTANT: Call `search_opportunities` first with the opportunity's name to avoid creating duplicates. Affinity does not deduplicate on create. NOTE: Custom field values cannot be set during creation. If the user wants to set field values on the new Opportunity, follow up with `get_list_fields` to discover field IDs and `upsert_list_entry_field_values` to set them on the list entry returned in `list_entries[0]`. Args: name: Name of the Opportunity. list_id: ID of the opportunity-type list. company_ids: IDs of existing companies to associate with the new Opportunity. person_ids: IDs of existing persons to associate with the new Opportunity. Returns: JSON object containing the newly created Opportunity. Top-level keys: `id`, `name`, `organization_ids` (list[int]), `person_ids` (list[int]), `list_entries` (list).
Creates a new person (contact) in Affinity. Use for adding people not yet in the CRM, e.g. importing from a CSV or a hand-entered contact. IMPORTANT: - Call `search_persons` first with the person's name or email to avoid creating duplicates. Affinity does not deduplicate on create. - `company_ids` takes Affinity company IDs (integers), not company names or domains. If the user provides a company name or domain, resolve it via `search_companies_top_matches` first and pass the resulting IDs. If unclear, ask before calling. Args: first_name: Required. The person's first name. last_name: Required. The person's last name. emails: Required. List of email addresses for the person. Pass an empty list `[]` if none are known. Example: `["alice@affinity.co"]`. company_ids: Optional list of Affinity company IDs the person is associated with. Example: `[1687449]`. Returns: The newly created person object including its assigned `id`, `type`, `first_name`, `last_name`, `primary_email`, `emails`, and `organization_ids`.
Creates a new reminder. SCHEDULING BY DAY NAME: If the user specifies a recurring reminder by day name (e.g. "every Friday", "every Sunday"), calculate the next occurrence of that day from today's date and use it as the due_date for the first reminder. Set reminder_days to match the recurrence interval (e.g. 7 for weekly, 14 for bi-weekly). You must still supply reset_type and the other recurring fields documented under reset_type and reminder_days below; this section only helps derive due_date and reminder_days. Args: owner_id: Required. Internal person ID who owns the reminder (team member only; not an external contact). reminder_type: Required. ReminderType.ONE_TIME (0) or ReminderType.RECURRING (1). If unclear, ask before calling. entity_id: Required with entity_type. ID of the company, external/collaborator person, or opportunity to attach the reminder to. If the user has not chosen a target yet, ask before calling. entity_type: Required with entity_id. Options: 0 = person, 1 = company, 8 = opportunity. content: Optional description or note text for the reminder. due_date: Required when reminder_type is ONE_TIME; omit for recurring unless you are supplying a first due date (e.g. day-name scheduling). ISO 8601 timestamp at noon UTC. If the user has not given a due date for a one-time reminder, ask before calling. The API stores due_date as an instant and renders it in the user's local timezone, so the chosen instant must land on the correct calendar day after that rendering. Use noon UTC (e.g. "2025-01-31T12:00:00Z") -- it lands on the same calendar day in every timezone from UTC-12 through UTC+11. Do not use UTC midnight ("2025-01-31T00:00:00Z"): it rolls back to the previous day in every Western timezone. Example: "2025-01-31T12:00:00Z" reset_type: Required when reminder_type is RECURRING. What triggers the reset: ReminderResetType.INTERACTION (0), EMAIL (1), or EVENT (2). If missing, ask before calling. reminder_days: Required when reminder_type is RECURRING; minimum 1. If missing, ask before calling. is_completed: Optional; only for ONE_TIME. Whether the reminder is completed on creation. Do not pass for RECURRING (the API rejects it). Returns: dict[str, Any]: API response containing the created reminder.
Delete a dropdown option from a list's dropdown field, e.g. removing an outdated "Associate" option from a Seniority field. IMPORTANT: - Deleting an option cascades: it is removed from every list entry that had it selected. This cannot be undone. - This does not work on status fields, which Affinity manages separately; the API returns a 400 for them. - Call `get_list_field_dropdown_options` first to get the dropdown_option_id. Args: list_id: ID of the list the field belongs to. field_id: Human-readable field ID, e.g. "field-1234". dropdown_option_id: ID of the dropdown option to delete. Returns: None. The API returns 204 No Content on success.
Deletes a reminder by ID. Requires the current user to be either the creator OR owner of the reminder, otherwise the API returns a permissions error. Args: reminder_id: The ID of the reminder to delete. Returns: JSON object containing confirmation of successful deletion.
Returns basic information and non-list specific field data on the requested company. By default, returns all field data including location, description, and more. Use get_entity_fields tool if you need to filter to specific fields. IMPORTANT: field_ids and field_types are mutually exclusive. They cannot be used together. Args: company_id (required): The ID of the company field_ids: List of field IDs to return. field_types: List of field types to return. Defaults to ['enriched', 'global', 'relationship-intelligence'] Returns: Metadata about the specified company including field data
Fetches list entries for the given company. Args: company_id (required): The ID of the company cursor: Cursor for the next or previous page. limit: The number of items to include in the page. Default is 100 Returns: Structured data containing the matching list entries
Get the relationship strengths between your team and the people associated with a company ("who do we know at this company"). Returns person-to-person relationships, not a single company-level score: for the given company, each result pairs an internal team member (person1) with an external contact who works at that company (person2), plus the strength of their relationship. Results are always ordered by strength (highest first), then by id, unless overridden with `order_by`. Relationships with a strength of 0 and no LinkedIn connection, and externals hidden in your org, are excluded. Each result's `interactionScore` is a float between 0 and 1. Convert it to a percentage between 0 and 100, rounded to the nearest integer (.5 or greater rounds up, less than .5 rounds down) when presenting it to the user. A relationship derived purely from a LinkedIn connection, with no interaction history, has an interactionScore of 0. Each result also has a `linkedIn` field: `{"connectedOn": <date>}` when the two people are connected on LinkedIn (whether or not the relationship also has interaction history), or `null` when there is no LinkedIn connection between them. Supported filter property (FIQS syntax): - interactionScore (>, <, >=, <=) e.g. "interactionScore > 0.5" Args: company_id: The ID of the company to get relationships for. cursor: Opaque pagination cursor extracted from a previous response's pagination.nextUrl (the cursor query param, not the full URL). limit: Number of items per page (1-100). Default is 100. filter_expr: FIQS filter string to narrow results by interactionScore. Example: "interactionScore >= 0.7". order_by: Sort order. "interactionScore" (ascending) or "-interactionScore" (descending). Omit to keep the default order. total_count: When true, the response pagination object includes the total number of matching relationships. Default is false. Returns: JSON object with: - data: list of relationship objects, each containing person1 (the internal team member) and person2 (the external contact at the company), each with id, firstName, lastName, and primaryEmailAddress, plus interactionScore (float between 0 and 1) and linkedIn ({"connectedOn": <date>} or null). - pagination: object with prevUrl and nextUrl (each null when there is no such page); totalCount is included only when total_count is true.
Find warm intro paths to a company through former coworkers: people at your org whose work history overlaps with people currently at the target company. These are INFERRED second-degree connections derived from shared employment history, not from your org's own interaction history. The people surfaced may never have emailed or met anyone at your org. For relationship strengths based on your org's actual interactions ("who do we know at this company"), use `get_company_relationships` or `get_person_relationships` instead. IMPORTANT: `company_id` is an Affinity company ID, not a name or domain. Call `search_companies_top_matches` first to resolve a company name to its ID. An empty `data` list can mean there are no inferred connections for the company, or that your organization does not have access to inferred connections; the API enforces access, this tool does not distinguish the two. Args: company_id: The ID of the target company whose people to find former coworkers of. cursor: Opaque pagination cursor extracted from a previous response's pagination.nextUrl (the cursor query param, not the full URL). limit: Number of target-person groups per page (1-50), not individual connections. Default is 20. Values outside 1-50 raise ValueError before any API call. total_count: When true, the response pagination object includes the total number of matching groups. Default is false. Returns: JSON object with: - data: list of groups, one per person at the target company. Each group contains the target person and a `connections` array; each connection pairs a person at your org with the inference that links them, including `sharedEmployer` (the company where they overlapped) and `overlapStartDate` / `overlapEndDate` for the overlap period. - pagination: object with prevUrl and nextUrl (each null when there is no such page); totalCount is included only when total_count is true.
Get information about the current user. Use this tool to verify authentication and understand available API access levels. Returns: Information about the user, their current organization, and API key permissions.
Find entities attached to a note. Allows to retrieve details of a note, or a list of entities that are directly attached to a note. Args: note_id: The note ID that will be used in the search entity_type: Type of entities to retrieve. Options: 0 = person, 1 = company, 8 = opportunity.
Returns the dropdown options for a dropdown or ranked-dropdown field on a company or person. Use the returned dropdown option IDs when writing dropdown field values via upsert_entity_field_values. Company and person fields are global to the org, so options are resolved from the field alone; no entity ID is needed. Args: entity_type: 0 = person, 1 = company. field_id: human-readable string ID of field to return options for (ex. "field-123") cursor: Cursor for the next or previous page. limit: The number of items to include in the page. Default is 20, maximum is 100. Returns: Metadata about each dropdown option
Returns metadata on non-list specific company or person fields. Args: entity_type: Type of entity to return fields for. Options: 0 = person, 1 = company cursor: Cursor for the next or previous page. limit: The number of items to include in the page. Default is 100. filter: Filter fields by name. Supports two operators, both case-sensitive, so match the field's actual capitalization: - Exact match: name="Location" - Substring match: name=~"Funding" Wrap any value containing spaces in double quotes, e.g. name=~"Last Funding" includes: Extra per-field metadata to return, omitted by default. Request these before building a search_companies_top_matches, search_all_companies, or search_persons filter or sort to confirm the field supports the operator and ordering you intend. - FieldInclude.FILTERABILITY: how the field can be filtered. - FieldInclude.SORTABILITY: whether and how the field can be sorted. See Returns for the shape of each and how to map it onto a search_companies_top_matches, search_all_companies, or search_persons filter or sort. Returns: Field metadata. Each field includes id, name, type, valueType, and enrichmentSource. When requested via `includes`, each field also carries `filterability` and/or `sortability`, describing how (if at all) it can be used in a search_companies_top_matches, search_all_companies, or search_persons filter or sort. filterability - how the field can be filtered, or null if it cannot be: - filterableFieldType: "field-only" to filter the field directly, or "attribute-on-field" to filter one of its sub-attributes instead. - operators: the filter operators the field allows. Each operator's `id` (e.g. "is-any-of") is the value to use as the filter `operator`. `name` is human-readable, `numberOfValuesRequired` (none/one/two/multi) is how many values the operator takes, and `relativeDateUnits` lists the time units allowed for relative-date operators. - attributes: only for "attribute-on-field". Each sub-attribute has an `id` (use as the filter `attributeId`), `name`, `valueType` (use as the filter `valueType`), and its own `operators`. sortability - how the field can be sorted, or null if it cannot be: - sortableFieldType: "field-only" to sort by the field directly, or "attribute-on-field" to sort by one of its sub-attributes instead. - attributes: only for "attribute-on-field". Each has an `id` (use as the sort `attributeId`), `name`, and `valueType`.
Get a paginated list of field value changes across the organization. Designed for delta sync: on first call fetch all changes, then store pagination.nextCursor and pass it to subsequent calls to retrieve only new changes since the last sync. Supported filter properties (FIQS syntax, combine with & and |): - field.id (=) e.g. "field.id=field-1234" - listEntry.id (=) e.g. "listEntry.id=5678" - changer.id (=) e.g. "changer.id=9012" - changedAt (>, <, >=, <=) e.g. "changedAt>=2025-01-01T00:00:00Z" - actionType (=) one of: add, update, delete Args: cursor: Cursor for the next or previous page. Pass the value of `pagination.nextCursor` or `pagination.prevCursor` from a prior response. Do NOT parse or modify the cursor string. limit: Number of items per page (1-100). Default is 100. filter_expr: FIQS filter string to narrow results. order_by: Sort order. "changedAt" (ascending, default) or "-changedAt" (descending). Returns: JSON object with: - data: list of field value change objects, each containing id, field, entity, listEntry, changer, changedAt, actionType, type, and value. - pagination: object with `nextCursor` / `prevCursor` strings (pass directly to the `cursor` param) and `nextUrl` / `prevUrl` full URLs. The value shape depends on the type discriminator field: - text, filterable-text, filterable-text-multi: string - number, number-multi: float - datetime: ISO 8601 string - location, location-multi: {streetAddress, city, state, country, continent} - person, person-multi: {referenceType, id, firstName, lastName, primaryEmailAddress, type} or {referenceType: "deleted-entity", displayValue} - company, company-multi: {referenceType, id, name, domain} or {referenceType: "deleted-entity", displayValue} - dropdown, dropdown-multi: {referenceType, id, value} or {referenceType: "deleted-entity", displayValue} - ranked-dropdown: {referenceType, id, value, rank} or {referenceType: "deleted-entity", displayValue}
Find warm intro paths to a company through investor-executive connections: investors at your firm who are connected to executives at the target company via a shared portfolio company. These are INFERRED second-degree connections derived from investment relationships, not from your org's own interaction history. The people surfaced may never have emailed or met anyone at your org. For relationship strengths based on your org's actual interactions ("who do we know at this company"), use `get_company_relationships` or `get_person_relationships` instead. IMPORTANT: `company_id` is an Affinity company ID, not a name or domain. Call `search_companies_top_matches` first to resolve a company name to its ID. An empty `data` list can mean there are no inferred connections for the company, or that your organization does not have access to inferred connections; the API enforces access, this tool does not distinguish the two. Args: company_id: The ID of the target company whose executives to find connections to. cursor: Opaque pagination cursor extracted from a previous response's pagination.nextUrl (the cursor query param, not the full URL). limit: Number of target-person groups per page (1-50), not individual connections. Default is 20. Values outside 1-50 raise ValueError before any API call. total_count: When true, the response pagination object includes the total number of matching groups. Default is false. Returns: JSON object with: - data: list of groups, one per person at the target company. Each group contains the target person and a `connections` array; each connection pairs an investor at your org with the inference that links them, including `investingFirm` and `portfolioCompany` (the shared portfolio company behind the connection). - pagination: object with prevUrl and nextUrl (each null when there is no such page); totalCount is included only when total_count is true.
Returns the dropdown options for a specific dropdown or ranked-dropdown field on a list. Use the returned dropdown option IDs when writing dropdown field values via upsert_list_entry_field_values. Args: list_id: ID of list field_id: human-readable string ID of field to return options for (ex. "field-123") cursor: Cursor for the next or previous page. limit: The number of items to include in the page. Default is 20, maximum is 100. Returns: Metadata about each dropdown option
Returns metadata on fields available for a given list. Args: list_id: ID of list to return fields for. cursor: Cursor for the next or previous page. limit: The number of items to include in the page. Default is 100. filter: Filter fields by name. Supports two operators, both case-sensitive, so match the field's actual capitalization: - Exact match: name="Location" - Substring match: name=~"Funding" Wrap any value containing spaces in double quotes, e.g. name=~"Last Funding" includes: Extra per-field metadata to return, omitted by default. Request these before building a search_list_entries filter or sort to confirm the field supports the operator and ordering you intend. - FieldInclude.FILTERABILITY: how the field can be filtered. - FieldInclude.SORTABILITY: whether and how the field can be sorted. See Returns for the shape of each and how to map it onto a search_list_entries filter or sort. Returns: Field metadata. Each field includes id, name, type, valueType, and enrichmentSource. When requested via `includes`, each field also carries `filterability` and/or `sortability`, describing how (if at all) it can be used in a search_list_entries filter or sort. filterability - how the field can be filtered, or null if it cannot be: - filterableFieldType: "field-only" to filter the field directly, or "attribute-on-field" to filter one of its sub-attributes instead. - operators: the filter operators the field allows. Each operator's `id` (e.g. "is-any-of") is the value to use as the filter `operator`. `name` is human-readable, `numberOfValuesRequired` (none/one/two/multi) is how many values the operator takes, and `relativeDateUnits` lists the time units allowed for relative-date operators. - attributes: only for "attribute-on-field". Each sub-attribute has an `id` (use as the filter `attributeId`), `name`, `valueType` (use as the filter `valueType`), and its own `operators`. sortability - how the field can be sorted, or null if it cannot be: - sortableFieldType: "field-only" to sort by the field directly, or "attribute-on-field" to sort by one of its sub-attributes instead. - attributes: only for "attribute-on-field". Each has an `id` (use as the sort `attributeId`), `name`, and `valueType`.
Get metadata on a single list Args: list_id (required): ID of list Returns: JSON object containing list metadata such as list name, entity type, creator ID, and owner ID.
Get all lists in the user's organization that they have access to, optionally filtered by name. Args: term: Case-insensitive substring filter on list name. Returns lists whose name contains this string anywhere. Not fuzzy - typos will miss; if results are empty, try a shorter or different keyword. cursor: Cursor for the next or previous page. limit: The number of items to include in the page. Default is 100. Returns: List of JSON objects containing list metadata such as list name, entity type, creator ID, and owner ID.
Get information about past and future meeting interactions and their attendees. Args: filter_expr: Filter expression using Affinity query language. Available fields: - id: unique identifier for the meeting (operators: =) Example: "id=1" or "(id=1 | id=2)" - startTime: When the meeting was scheduled (operators: >, <, >=, <=) Format: ISO 8601 timestamp Example: "startTime>2025-01-01T01:00:00Z" - createdAt: When the meeting was created in Affinity (operators: >, <, >=, <=) Format: ISO 8601 timestamp Example: "createdAt>=2025-01-01T00:00:00Z" - updatedAt: When the meeting was last updated (operators: >, <, >=, <=) Format: ISO 8601 timestamp Example: "updatedAt<=2025-12-31T23:59:59Z" Complex examples: - Date range: "startTime>=2025-01-01T00:00:00Z & startTime<2025-02-01T00:00:00Z" - Multiple date filters: "startTime>2025-01-01T00:00:00Z & createdAt>=2024-12-01T00:00:00Z" Boolean logic: & (AND), | (OR), () for grouping Full filtering spec: https://developer.affinity.co/pages/external-api-v2/filtering cursor: Cursor for the next or previous page. limit: Number of items to include in the page. Default is 20, max is 100. Returns: Structured data containing the list of meetings and pagination information. Raises: MeetingsV2AccessError: If the user does not have access to V2 meetings endpoint
Get information about meetings for a specific company, person, or opportunity. IMPORTANT: The date range between start_time and end_time cannot exceed 365 days. Be mindful of leap years. Args: entity_id: The ID of the company, external person, or opportunity entity_type: What type of entity this ID refers to. Options: 0 = person, 1 = company, 8 = opportunity start_time: ISO 8601 formatted timestamp to filter meetings starting from this time. Must be before end_time Format: "2025-01-01T00:00:00Z" end_time: ISO 8601 formatted timestamp to filter meetings ending before this time. Must be after start_time Format: "2025-01-01T00:00:00Z" Maximum 365 day range examples: ✓ VALID: start_time: "2025-02-25T00:00:00Z", end_time: "2026-02-25T00:00:00Z" ✓ VALID: start_time: "2025-01-01T00:00:00Z", end_time: "2025-12-31T23:59:59Z" ✓ VALID (leap year): start_time: "2024-01-01T00:00:00Z", end_time: "2024-12-31T00:00:00Z" (365 days, not 366) ✗ INVALID: start_time: "2025-02-25T00:00:00Z", end_time: "2026-02-25T23:59:59Z" (exceeds 1 year by 23:59:59) internal_person_id: ID of an internal person that was involved in the meetings. This parameter filters down the set of interactions related to the given entity to only those in which this internal person was involved. Cannot be used to find all of an internal person's interactions. logging_type: Filter by logging classification. When not supplied, returns all logging types. Options: - LoggingType.ALL (0): Both automatically and manually logged interactions - LoggingType.MANUAL (1): Only manually logged interactions page_size: Number of results per page. Default is 10, maximum is 100. page_token: The next_page_token from the previous response required to retrieve the next page of results. Returns: Structured data containing an array of meetings and pagination for retrieving more results. Each meeting includes a `v2_id` field when the unified meetings feature gate is enabled for the caller's org, and a `notes` array holding any notes already on the meeting.
Get all notes attached to a specific company, person, or opportunity. For the reverse (finding entities attached to a note), use get_entities_attached_to_note. Args: entity_id: The ID of the company, person, or opportunity entity_type: What type of entity this ID refers to. Options: 0 = person, 1 = company, 8 = opportunity Example: To get notes for company ID 123, use entity_id=123, entity_type=1
Returns basic information and non-list specific field data on the requested person. By default, returns all field data including current organization, job title, and more. Use get_entity_fields tool if you need to filter to specific fields. IMPORTANT: field_ids and field_types are mutually exclusive. They cannot be used together. Args: person_id (required): The ID of the person field_ids: list of field IDs to return. field_types: List of field types to return. Defaults to ['enriched', 'global', 'relationship-intelligence'] Returns: Metadata about the specified person including field data
Fetches list entries for the given person. Args: person_id (required): The ID of the person cursor: Cursor for the next or previous page. Extract from the `cursor` query parameter in the previous response's `pagination.nextUrl` or `pagination.prevUrl`. limit: The number of items to include in the page. Default is 100 Returns: Structured data containing the matching list entries
Get the relationship strengths between a person and the people they interact with (relationship intelligence / "how do I know them"). Works for both internal team members and external contacts: - Pass an internal person's ID to see their relationships with external contacts. - Pass an external person's ID to see which internal team members know them. Results are always ordered by strength (highest first), then by id, unless overridden with `order_by`. Relationships with a strength of 0 and no LinkedIn connection, and externals hidden in your org, are excluded. Each result's `interactionScore` is a float between 0 and 1. Convert it to a percentage between 0 and 100, rounded to the nearest integer (.5 or greater rounds up, less than .5 rounds down) when presenting it to the user. A relationship derived purely from a LinkedIn connection, with no interaction history, has an interactionScore of 0. Each result also has a `linkedIn` field: `{"connectedOn": <date>}` when the two people are connected on LinkedIn (whether or not the relationship also has interaction history), or `null` when there is no LinkedIn connection between them. Supported filter property (FIQS syntax): - interactionScore (>, <, >=, <=) e.g. "interactionScore > 0.5" Args: person_id: The ID of the person (internal or external) to get relationships for. cursor: Opaque pagination cursor extracted from a previous response's pagination.nextUrl (the cursor query param, not the full URL). limit: Number of items per page (1-100). Default is 100. filter_expr: FIQS filter string to narrow results by interactionScore. Example: "interactionScore >= 0.7". order_by: Sort order. "interactionScore" (ascending) or "-interactionScore" (descending). Omit to keep the default order. total_count: When true, the response pagination object includes the total number of matching relationships. Default is false. Returns: JSON object with: - data: list of relationship objects, each containing person1 (the internal side) and person2 (the external side), each with id, firstName, lastName, and primaryEmailAddress, plus interactionScore (float between 0 and 1) and linkedIn ({"connectedOn": <date>} or null). - pagination: object with prevUrl and nextUrl (each null when there is no such page); totalCount is included only when total_count is true.
Returns basic information and non-list specific field data on multiple requested persons in a single call. Use this instead of calling get_person_info once per person when you already know the exact person IDs you need, e.g. contacts surfaced by get_person_relationships or get_company_relationships. Unlike get_person_info, this does not return all field data by default. Use get_entity_fields to discover field IDs/types, then pass them via field_ids or field_types. IMPORTANT: field_ids and field_types are mutually exclusive. They cannot be used together. Args: person_ids (required): The IDs of the persons. Best for up to ~100 per call (the endpoint's page size cap); use cursor for more. field_ids: list of field IDs to return. field_types: List of field types to return. Unlike get_person_info, this has no default: field data is empty for every person unless field_ids or field_types is passed. cursor: Cursor for the next or previous page. limit: The number of items to include in the page. Default is 100. total_count: If True, include the total match count in pagination. Returns: Metadata about the specified persons including field data
Returns reminders that meet the query parameters if provided. By default, returns reminders for all entities. IMPORTANT: entity_id and entity_type are only applied when both are provided; if only one is set, the entity filter is ignored and reminders for all entities are returned. Args: entity_id: The ID of the company, external person, or opportunity to filter reminders by. entity_type: The type of entity that entity_id refers to. Options: 0 = person, 1 = company, 8 = opportunity. creator_id: Filters to reminders created by this internal person (identified by their person ID). owner_id: Filters to reminders assigned to this internal person (identified by their person ID). completer_id: Filters to reminders completed by this internal person (identified by their person ID). reminder_type: Filters by recurrence type: - ReminderType.ONE_TIME (0): One-time reminders only - ReminderType.RECURRING (1): Recurring reminders only reset_type: Filters recurring reminders by their reset trigger. Only valid when reminder_type=RECURRING. - ReminderResetType.INTERACTION (0): Resets on any interaction - ReminderResetType.EMAIL (1): Resets on email only - ReminderResetType.EVENT (2): Resets on event only status: Filters by reminder status: - ReminderStatus.COMPLETED (0): Completed reminders - ReminderStatus.ACTIVE (1): Active reminders - ReminderStatus.OVERDUE (2): Overdue reminders due_before: ISO 8601 formatted timestamp to retrieve reminders due before this point. Example: "2025-01-31T12:00:00Z" due_after: ISO 8601 formatted timestamp to retrieve reminders due after this point. Example: "2025-01-01T12:00:00Z" page_size: Number of results per page. Default is 10. page_token: The next_page_token from the previous response required to retrieve the next page of results. Returns: Structured data containing the list of reminders and pagination information.
Paginate through list entries on a given saved view. Each list entry contains basic information about the related person/company/opportunity along with the field data that the saved view has been configured to display. Unlike search_list_entries, field selection is not supported here: the saved view itself defines which fields (columns) are returned. Use get_saved_views to discover the view_id for a list. Args: list_id (required): ID of the list. view_id (required): ID of the saved view on that list. cursor: Cursor for the next or previous page. Pass the value of `pagination.nextCursor` or `pagination.prevCursor` from a prior response. Do NOT parse or modify the cursor string. limit: The number of items to include in the page. Default is 10 to keep response size manageable, maximum is 100. Page through with cursor for more entries. Returns: Data array of list entries (entity info plus the view's configured fields) plus a pagination object with `nextCursor` / `prevCursor` strings (pass directly to the `cursor` param), `nextUrl` / `prevUrl` full URLs.
Paginate through all saved views the user has access to for a specific List. Args: list_id (required): ID of the list. cursor: Opaque cursor token for the next or previous page. Pass the value of the `cursor` query parameter extracted from the previous response's `pagination.nextUrl` or `pagination.prevUrl`, not the full URL. limit: The number of items to include in the page. Default is 100, maximum is 100. Returns: Data array of saved views (each containing id, name, type, createdAt) plus a pagination object with `prevUrl` and `nextUrl`. Those are full URLs; the next cursor value is the `cursor` query parameter embedded in them.
Retrieve a single list entry from the given list. The list entry contains basic information about the related person/company/opportunity and list-specific field data. By default, returns only list-specific fields. IMPORTANT: field_ids and field_types are mutually exclusive. Passing both raises ValueError. WARNING: List entry may contain large field data. Best practices to minimize response size: - Select specific fields using field_ids or field_types (use get_list_fields tool to identify fields) - Request additional field types (enriched, global, relationship-intelligence) only when needed Args: list_id (required): ID of list list_entry_id (required): ID of list entry field_ids: List of field IDs to return. field_types: List of field types to return. Defaults to ['list']. Available types: 'enriched', 'global', 'relationship-intelligence', 'list' Returns: JSON object for the single list entry: entity info (person/company/opportunity) and the selected list-specific field values.
Get dialogue fragments (individual speaker turns) from a transcript. Use pagination to retrieve all fragments from long transcripts. To get transcripts for a specific entity (company, person, opportunity): 1. Use get_notes_for_entity to get notes for that entity 2. Look for notes where type is "ai-notetaker" or "ai-notetaker-reply" 3. Those notes have a transcriptId field - use it with this tool Args: transcript_id: The transcript ID from the transcriptId field in AI Notetaker notes (returned by get_notes_for_entity). cursor: Cursor for the next or previous page. limit: Number of items per page. Default is 20, max is 100. Returns: Paginated list of dialogue fragments with total count. Each fragment contains speaker, content, and timestamps.
Get all users (internal team members) in your Affinity organization. Use this to list, search, or paginate through internal Affinity users, not external contacts. To look up only the authenticated caller, use get_current_user instead. Args: term: Case-insensitive search across first name, last name, and primary email address. Example: "jane" or "jane@affinity.co". filter_expr: Filter expression using Affinity query language. Supported fields: - id: unique identifier for the user (operators: =) Example: "id=1" or "(id=1 | id=2)" - status: the user's account status (operators: =). Values: active, invited, deactivated Example: "status=active" Boolean logic: & (AND), | (OR), () for grouping Full filtering spec: https://developer.affinity.co/pages/external-api-v2/filtering cursor: Opaque pagination cursor extracted from a previous response's pagination.nextUrl or prevUrl (the cursor query param, not the full URL). limit: Number of items to include in the page (1-100). Default is 100. Returns: A dict with `data` (list of user objects, each with id, firstName, lastName, primaryEmailAddress, photoUrl, and status; emailAddresses and role are included only when the caller has the "Manage Users" permission) and `pagination` (prevUrl and nextUrl, each null when there is no such page; extract the `cursor` query param from either URL to pass to a subsequent call).
Find notes in Affinity. Filter by ID, creator, and/or creation date. Args: filter_expr: Filter expression using Affinity query language. Available fields: - id: unique identifier for the note (operators: =) Example: "id=123" or "(id=1 | id=2 | id=10)" - creator.id: Person ID that created the note (operators: =) Example: "creator.id=456" - createdAt: When the note was created in Affinity (operators: >, <, >=, <=) Format: ISO 8601 timestamp Example: "createdAt>=2026-01-01T00:00:00Z" Complex examples: - Date range: "createdAt>=2026-01-01T00:00:00Z & createdAt<2026-02-01T00:00:00Z" - By creator in date range: "creator.id=123 & createdAt>=2026-01-01T00:00:00Z" Boolean logic: & (AND), | (OR), () for grouping Full filtering spec: https://developer.affinity.co/pages/external-api-v2/filtering cursor: Cursor for the next or previous page. limit: Number of items to include in the page. Default is 20, max is 100. When you need more than 100 records, use the cursor to paginate over the results. Returns: Structured data containing the list of notes and pagination information.
Render warm intro paths as an interactive network-map graph. IMPORTANT: only call this when the user asks to see, map, visualize, or diagram the paths. Never render automatically. Call this only after finding and ranking paths with get_person_relationships / get_company_relationships and processing them per the warm intro skill (Steps 2-3). This tool does not fetch or rank anything itself. It draws exactly the paths it is given, so pass the same de-duplicated, capped, scored paths already shown to the user in text. Never invent a node, edge, or score not already in those results. Every edge is an explicit, scored relationship pulled directly from Affinity data (get_person_relationships / get_company_relationships). `targets` is always one or more actual people: a specific-person ask gives you one target, and an "anyone at the company" ask gives you the list of real candidate contacts the skill actually found, each with their own direct edge to a teammate. The tool marks the single highest-`score_pct` edge as recommended in the returned data. This keeps "recommended" tied purely to relationship strength. Example, one teammate with a single path to one target: team = [{"id": 1, "name": "Jamie Chen"}] targets = [{"id": 100, "name": "Sam Altman", "title": "CEO"}] edges = [{"teammate_id": 1, "target_id": 100, "score_pct": 90}] Args: team: Internal teammates who could make the introduction. Every entry must be referenced by at least one edge. Maximum 5. targets: The destination, one or more real people internal teammates have connections to. Maximum 5. edges: Ranked paths, matching the warm intro skill's Step 3 cap of 5 total, sorted descending by `score_pct`. The tool renders the order given and does not re-rank. Maximum 5. Each (teammate_id, target_id) pair must be unique. De-duplicate paths before calling this tool. Returns: JSON object with `team`, `targets`, and `edges` for the MCP App to render. Each edge in the returned `edges` also carries a computed `recommended` boolean (see above). Text-only clients receive the same object as plain JSON.
Paginate exhaustively through companies using structured filters, sorts, and/or a keyword. Use this tool when: - The request signals exhaustive intent ("all", "every", "full list", "export") - The user needs field data returned inline with results (field_ids or field_types) - The user needs a total count of matching companies (total_count=True) - Paginating through a complete result set from a previous call For all other searches, including structured-only filters, use search_companies_top_matches() instead. Omitting search_criteria returns every company with default pagination. FIELD SELECTION: field_ids and field_types are mutually exclusive; passing both raises ValueError. Valid field_types are enriched, global, and relationship-intelligence. When both are omitted, only company identity (id, name, domain, domains, isGlobal) is returned. DISCOVERING FIELDS: Use get_entity_fields(entity_type=EntityType.COMPANY, includes=[FieldInclude.FILTERABILITY, FieldInclude.SORTABILITY]) to find field IDs, valueTypes, supported operators, and any required attributeId. EXAMPLE - all companies not contacted in over 90 days, sorted by last contact: search_criteria = { "filters": { "operator": "and", "filters": [{ "fieldId": "last-contact", "attributeId": "date-of-activity", "valueType": "date", "operator": "is-not-within-the-last", "value": {"amount": [90], "unit": "day"}, }], }, "sorts": [{"fieldId": "last-contact", "attributeId": "date-of-activity", "direction": "asc"}], } Args: search_criteria: Optional filters, sorts, and/or keyword. Omit to paginate through every company. field_ids: Specific field IDs to include per company. Mutually exclusive with field_types. field_types: Field categories to include per company (enriched, global, relationship-intelligence). Mutually exclusive with field_ids. cursor: Cursor for the next or previous page. limit: Results per page. Default 10, max 100. total_count: If true, include the total number of matching companies in the response. Returns: A dict with `data` (list of company objects), `nextCursor` and `prevCursor` for pagination, and optionally `totalCount`.
Search companies using natural language, structured criteria, or both simultaneously. When query and search_criteria are provided, both semantic search and structured search run in parallel and results are merged. Call get_company_info for richer field data on results. DEFAULT for all company lookups, including structured-only filters with no semantic query. Only route to search_all_companies() when the request contains an explicit exhaustive-intent word such as "all", "every", "full list", "complete list", or "export", or when the user needs field data returned inline with results or a total count. Use this tool when discovery phrasing is present ("what companies", "which companies", "find companies", "show me companies"). By default, ALWAYS populate query and search_criteria together as they are complementary, except in the scenarios described below: WHEN TO OMIT QUERY: - Exact name or domain lookup: use search_criteria.search.term instead - Third-party enrichment data (Crunchbase, PitchBook, Dealroom): structured only - Company valuation and revenue metrics: structured only - Custom org fields: structured only - Pure deterministic field-value match: the request names a specific known value (e.g. "cf-funding-stage = Series B") and semantic adds no value WHEN TO OMIT SEARCH CRITERIA: - Firm-wide relationship scores (ex. "companies where we have a strong relationship") WHEN TO USE BOTH QUERY AND SEARCH CRITERIA For all items below, set query to describe the user's intent AND build a matching search_criteria filter (call get_entity_fields first). Both are required. - Industry/sector: "healthcare", "fintech", "climate tech", "enterprise SaaS", "biotech", "AI" - Technology/business concepts: "solar energy", "payment processing", "gene therapy" - Funding: stage, amount raised, funding date, year founded - Employee metrics: headcount, hiring/departure rates, employee growth - Interaction history: when your firm met with or emailed companies - Time references: "last week", "recently", "this quarter", "past 6 months", "90 days" - Headquarters location: cities, states, countries, regions - Investor information: VC/PE firm names NOT SUPPORTED: - Lookup by similarity to a named company ("companies like OpenAI") - Notes, email body content, or file attachments BUILDING SEARCH_CRITERIA: Call get_entity_fields(entity_type=EntityType.COMPANY, includes=[FieldInclude.FILTERABILITY, FieldInclude.SORTABILITY]) before constructing any filter. This returns exact field IDs, valueTypes, supported operators, and dropdown values. Do not guess field IDs. The API silently returns no results for wrong values. LIST FILTERING: Pass list_ids to scope the semantic search query to companies on specific Affinity lists. Use get_lists to discover IDs. Maximum 100 IDs. Ignored when query is not provided. SORTING: - search_criteria for structured search supports up to 5 sorts - Semantic search ranks by relevance score. If the query describes an ordering preference (e.g. "sorted by last contact"), only one sortable attribute is allowed. - When both searches run, semantic results appear first in relevance order, followed by structured-only results. QUERY GOTCHAS: "I", "we", and "our firm" in query refer to firm-wide data, not personal interaction history. "In our pipeline" filters to org-relevant companies, not membership in a list named "Pipeline" -- use list_ids to filter by a specific list. Interaction and relationship data is always firm-wide; you cannot filter by a specific team member's individual history. EXAMPLE - industry filter (use both): query = "biotech companies" search_criteria = { "filters": {"operator": "and", "filters": [{ "fieldId": "affinity-data-industry", "valueType": "filterable-text-multi", "operator": "has-any-of", "value": ["Biotech", "Biotechnology"], }]}, } EXAMPLE - exact name lookup (structured only, no query): search_criteria = {"search": {"term": "Acme Corp"}} RESULT ORDERING: Semantic hits appear first, sorted by relevance score. Structured-only hits are appended after. Companies matched by both carry sources=["semantic", "structured"]. Args: query: Natural language prompt for the semantic search. 1-500 characters. search_criteria: Filters, sorts, and/or a keyword search term for the structured search. list_ids: Optional Affinity list IDs to scope the semantic search to. Raises ValueError when provided without query. limit: Maximum results to request from each source. Default 10, max 100. The merged response can contain up to 2*limit unique companies. Returns: A dict with: `companies`: list of company objects, each with id, name, domain, domains, isGlobal, score (relevance score or null), and sources (list containing "semantic", "structured", or both). `isGlobal: true` means the company comes from Affinity's global enrichment dataset and may not exist in your org's CRM. Mention this distinction when surfacing results to the user. `explanation`: natural language rationale from the semantic search API (only present when the semantic search ran and returned an explanation). `search_errors`: list of error messages for sub-searches that failed (only present when one search failed and the other succeeded). Results from the successful search are still returned.
Search files by keyword. Scope the search by passing either `file_ids` or `company_id`, or omit both to search across all files in the organization. Each result contains a matched file and a single representative excerpt. Even if a file contains multiple matching passages, it appears exactly once in the response. IMPORTANT: `file_ids` and `company_id` are mutually exclusive. They cannot be used together. Args: prompt: The search query. Returns up to `limit` files ordered by relevance. Prompts with no strong matches may still return low-relevance results. 3-500 characters. file_ids: Optional list of file IDs to restrict the search to. Maximum 100 ids. Mutually exclusive with `company_id`. company_id: Optional company ID to restrict the search to files associated with that company. Mutually exclusive with `file_ids`. limit: Number of matches to return. Default is 20. Minimum is 1, maximum is 100. Returns: Structured data with a `data` array. Each item contains the matching file (id, name), a `pageNumber` (populated for paginated formats like PDFs), and a `preview` snippet of the matched text. An empty `data` array may indicate that there were no matches, or that the organization does not have Deal Assist enabled.
Search list entries on a list using filters, sorts, and/or a keyword. Omitting `search_criteria` returns every entry on the list with default pagination. WARNING: Lists can contain many entries with large field data. To minimize response size, start with the default limit=10 (increase only if needed), use pagination (cursor) to iterate, and return only the fields you need (see FIELD SELECTION). FIELD SELECTION (which fields to return per entry): `field_ids` and `field_types` are mutually exclusive; passing both raises ValueError. When both are omitted, defaults to `field_types=['list']` (list-specific fields only). Request additional types (enriched, global, relationship-intelligence) only when needed. DISCOVERING FIELDS: Use get_list_fields to find the field IDs you can reference in filters, sorts, and search.fieldIds. Call it with includes=[FieldInclude.FILTERABILITY, FieldInclude.SORTABILITY] to learn which operators each field can be filtered with and whether it can be sorted (including any attributeId a field requires), so you only build filters and sorts the field supports. See FilterGroup, ValueFilter, SortCriterion, and SearchTerm for the structure and accepted values of each criterion type. EXAMPLE - filter by a dropdown field: search_criteria = { "filters": { "operator": "and", "filters": [{ "fieldId": "field-4574182", "valueType": "dropdown", "operator": "is-any-of", "value": [{"dropdownOptionId": 1}, {"dropdownOptionId": 2}], }], }, } EXAMPLE - filter by last-email within 30 days, sort by date added desc: search_criteria = { "filters": { "operator": "and", "filters": [{ "fieldId": "last-email", "attributeId": "date-of-activity", "valueType": "date", "operator": "is-within-the-last", "value": {"amount": [30], "unit": "day"}, }], }, "sorts": [{"fieldId": "created-at", "direction": "desc"}], } EXAMPLE - nested AND/OR (dropdown AND (employees>100 OR employees empty)): search_criteria = { "filters": { "operator": "and", "filters": [ {"fieldId": "field-4574182", "valueType": "dropdown", "operator": "is-any-of", "value": [{"dropdownOptionId": 1}]}, {"operator": "or", "filters": [ {"fieldId": "affinity-data-employees-current", "valueType": "number", "operator": "is-greater-than", "value": 100}, {"fieldId": "affinity-data-employees-current", "valueType": "number", "operator": "is-empty"}, ]}, ], }, } EXAMPLE - keyword search combined with a filter: search_criteria = { "search": {"term": "acme"}, "filters": { "operator": "and", "filters": [{ "fieldId": "field-4574182", "valueType": "dropdown", "operator": "is-not-empty", }], }, } EXAMPLE - filter where assigned-to has any of a few specific people: search_criteria = { "filters": { "operator": "and", "filters": [{ "fieldId": "field-assigned-to", "valueType": "person-multi", "operator": "has-any-of", "value": [{"id": 12345}, {"id": 67890}], }], }, } EXAMPLE - filter where a text field contains "term sheet": search_criteria = { "filters": { "operator": "and", "filters": [{ "fieldId": "field-notes-summary", "valueType": "text", "operator": "contains", "value": "term sheet", }], }, } Args: list_id: The ID of the list to search. search_criteria: Optional SearchCriteria with any of `filters`, `sorts`, `search`. See structure and examples above. Omit to paginate through every entry. field_ids: Specific field IDs to include per entry. Mutually exclusive with `field_types`. field_types: Field categories to include per entry. Mutually exclusive with `field_ids`. cursor: Cursor for the next or previous page. Pass the value of `pagination.nextCursor` or `pagination.prevCursor` from a prior response. Do NOT parse or modify the cursor string. limit: Results per page. Default 10, max 100. total_count: If True, include the total match count in pagination. Incurs extra query cost, use only when needed. Returns: A paginated response with `data` array containing list entries with entity info, selected field values, and `pagination` with `nextCursor` / `prevCursor` strings (pass directly to the `cursor` param), `nextUrl` / `prevUrl` full URLs, and optional `totalCount`.
Search notes by keyword. Scope the search by passing either `note_ids` or `company_id`, or omit both to search across all notes in the organization. Each result contains a matched note and a single representative excerpt. Even if a note contains multiple matching passages, it appears exactly once in the response. IMPORTANT: `note_ids` and `company_id` are mutually exclusive. They cannot be used together. Args: prompt: The search query. Returns up to `limit` notes ordered by relevance. Prompts with no strong matches may still return low-relevance results. 3-500 characters. note_ids: Optional list of note IDs to restrict the search to. Maximum 100 ids. Mutually exclusive with `company_id`. company_id: Optional company ID to restrict the search to notes associated with that company. Mutually exclusive with `note_ids`. limit: Number of matches to return. Default is 20. Minimum is 1, maximum is 100. Returns: Structured data with a `data` array. Each item contains the matching note id, its `kind` (one of: note, meeting-note, email-note, chat-message-note, meeting-ai-summary), and a `preview` snippet of the matched text. An empty `data` array may indicate that there were no matches, or that the organization does not have Deal Assist enabled.
Search for opportunities or list them with optional filters. Args: term: Search term used to search through opportunities page_size: Number of results per page. If not specified, defaults to 10 when searching with a term, otherwise 100. page_token: Pagination token for next page of results Returns: dict[str, Any]: API response containing information on list of opportunities
How do I improve a ChatGPT Plugin's discoverability?
The levers are the listing surface agents actually read: names, descriptions, keywords, tool metadata, and registry health. Which lever matters depends on where discovery breaks, which is what continuous measurement shows.
What are Affinity alternatives on ChatGPT?
As of 2026-08-14, Affinity competes with 4Degrees, Carta CRM, Rings AI in ChatGPT Investor & Deal-Flow Relationship CRM, ranked by public Discoverability Score.
Where is this profile measured?
This profile uses the geography attached to the latest public registry snapshot: US. Locale tags are intentionally omitted.