Affinity
Search, update, and prep deals
- Category
- Sales & CRM
- Primary Subcategory
- Investor & Deal-Flow Relationship CRM
Integration details
Description
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
- 75
- Geography
- US
The Primary Subcategory used for this profile’s headline score.
Other Subcategories where the Integration is listed.
Get alerts for Affinity
Get updates when Affinity’s Discoverability Score or category rank changes.
ChatGPT Plugin Discovery Score
ChatGPT Plugin discovery is coming soon
ChatGPT can surface a Plugin when it matches a user's request.Your Plugin Discovery Score measures how often yours appears.
No spam. Unsubscribe any time.
What discovery looks like

Competing in ChatGPT Investor & Deal-Flow Relationship CRM
View Category75 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 a dropdown or ranked-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. - If the user asks for a "dropdown" field without saying whether it should allow one selection or multiple, ask before calling this tool. Dropdown (17, allows_multiple supported) and ranked-dropdown (7, single-select only, supports ranking the options) are different field types with no shared upgrade path afterward. Don't assume either one. If the user's request already makes this clear (e.g. "multi-select dropdown", "ranked dropdown", "single choice"), proceed without asking. Args: name: The field name. Example: "Relationship Strength" value_type: The kind of value the field holds. Options: 0 = person, 1 = company, 3 = number, 4 = date, 5 = location, 6 = text, 17 = dropdown, 7 = ranked-dropdown (single-select only; does not support allows_multiple). 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, number, location, and dropdown (17) 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: `["[email protected]"]`. 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.
Permanently deletes one or more companies (organizations) from Affinity by company id. Confirmation gate: with confirm=false (the default) nothing is deleted. The response is a summary (warning, companies_preview, total, global_count, related_totals, next_step). Show the whole warning to the user in your own message, unquoted, keeping every company name it shows, the count, and the sentence that says what is kept: a short question alone hides how far the deletion reaches. Call again with confirm=true only after the user approves (or already approved this deletion in the conversation). A user who wants to see what the deletion affects does not need another delete_company call: read it yourself with get_company_list_entries, get_company_info and get_reminders. next_step carries the details. WARNING: this deletes the company record itself, not just a list membership. List entries on all lists, field values, and tagged reminders are deleted with it. Notes, files, people, and opportunities are kept. There is no undo. Global companies (enriched from Affinity's global dataset) cannot be deleted. global_count covers the whole batch. A confirmed call still tries every id and reports the global ones in failed with a 422. If the user removes a company from a list and you hold list entry ids, use delete_list_entries instead; the company record stays. Deleting a company also removes all its list entries. Never call both tools for the same company. company_ids are company ids, NOT list entry ids. Get them from search_companies_top_matches, get_company_info, the id returned by create_company, or the entity id of an entry on a company list (search_list_entries, get_single_list_entry); ask if the target is unclear. Args: company_ids: 1-100 company ids per call (ValueError outside that range). Repeated ids are removed before the preview and the deletion. confirm: false returns the summary only; true performs the deletion. Returns: confirm=false: the summary, nothing deleted. related_totals is {"list_entries": n, "partial": bool}: how many list entries the batch takes with it, partial=true when the number is a lower bound (an id could not be read, or a list is hidden from the API key). confirm=true: {"success", "deleted", "deleted_count", "failed": [{company_id, status_code, error}], "failed_count"}, one attempt per id. Unknown id: 422 (v1 has no 404). Global company: 422 "Cannot delete organization because it is global". API key without External API access: 403.
Deletes one or more list entries, removing those entities' rows from a list. Confirmation gate: with confirm=false (the default) nothing is deleted — the response is a summary of what would be deleted (warning, entries_preview, total, list_type, list_name, next_step). Present the warning to the user in your own message, unquoted, keeping every entry name, count, list name, and the sentence that says what the deletion does to the entity itself. A short confirmation question is not enough on its own: the user cannot tell how far the deletion reaches. Call again with confirm=true only after the user explicitly approves (or already approved this specific deletion in the conversation). For deletions spanning several lists, collect each list's summary first and ask once. WARNING: on an opportunity list, deleting an entry PERMANENTLY DELETES the underlying opportunity (requires manage permission, otherwise 403). On company/person lists only the membership is removed; the entity is untouched. If the user refers to the opportunity itself, use delete_opportunity. If the user refers to the company itself, and wants it removed from Affinity and not only from the list, use delete_company. If the user refers to the person themselves, and wants them removed from Affinity and not only from the list, use delete_person. If the user removes rows from a list and you hold list entry ids, this tool is correct, also on an opportunity list. Never call both tools for the same entity. list_entry_ids are the entries' own ids, NOT company/person/opportunity ids. Get them from create_list_entry, search_list_entries, get_company_list_entries, get_person_list_entries, or get_single_list_entry; ask if the target is unclear. Args: list_id: ID of the list the entries belong to. list_entry_ids: 1-100 entry ids per call (ValueError outside that range). Repeated ids are removed. confirm: false returns the summary only; true performs the deletion. Returns: confirm=false: the summary, nothing deleted. confirm=true: {"success", "deleted", "deleted_count", "failed": [{list_entry_id, status_code, error}], "failed_count"}, one attempt per entry. Invalid id: 422 (v1 has no 404).
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.
Permanently deletes one or more notes from Affinity by note id. Confirmation gate: with confirm=false (the default) nothing is deleted. The response is a summary (warning, total, next_step). Show the whole warning to the user in your own message, unquoted, keeping the count and the sentence that says what is kept: a short question alone hides how far the deletion reaches. Call again with confirm=true only after the user approves (or already approved this deletion in the conversation). A user who wants to see what the deletion affects does not need another delete_note call: read it yourself with query_notes and get_entities_attached_to_note. next_step carries the details. WARNING: the note's attached files go with it. Deleting a root note also deletes every reply under it, user and AI Notetaker replies alike; deleting a reply removes only that reply. Pass the root only: a reply listed in the same call as its root is already gone by the time its own DELETE runs and comes back as a 404 under failed, although the note is deleted. The persons, companies, and opportunities the note is attached to are kept. There is no undo, for admins either: the note is deleted outright, not archived. To fix a note's text or the records it is attached to instead, use update_note. You can only delete notes you created. Another user's note fails with 403 for that id, unless the organization has the admin-only-delete-any-note feature and the caller holds the manage-all-notes permission. note_ids are note ids. Get them from query_notes, get_notes_for_entity, search_notes, or the id returned by create_note; if two notes could be meant, resolve which one instead of guessing. Args: note_ids: 1-100 note ids per call (ValueError outside that range). Repeated ids are removed before the preview and the deletion. confirm: false returns the summary only; true performs the deletion. Returns: confirm=false: the summary, nothing deleted. confirm=true: {"success", "deleted", "deleted_count", "failed": [{note_id, status_code, error}], "failed_count"}, one attempt per id. Not your note: 403. Unknown or inaccessible id: 404. The other ids are still deleted.
Permanently deletes one or more opportunities (deals) from Affinity by opportunity id. Confirmation gate: with confirm=false (the default) nothing is deleted. The response is a summary of what would be deleted (warning, opportunities_preview, total, next_step). Present the warning to the user in your own message, unquoted, keeping every opportunity name, the count, and the sentence that says what is kept. A short confirmation question is not enough on its own: the user cannot tell how far the deletion reaches. Call again with confirm=true only after the user explicitly approves (or already approved this specific deletion in the conversation). WARNING: this deletes the opportunity record itself, not just a list membership. Its list entries, all list-specific field values and their change history, and any reminders tagged to it are deleted with it. Notes and files attached to it are kept but detached from it. There is no undo. If the user only wants to rename or re-associate an opportunity, use update_opportunity. If the user removes rows from a list and you hold list entry ids, use delete_list_entries instead, also on an opportunity list. Deleting an opportunity also removes its list entry. Never call both tools for the same opportunity. opportunity_ids are opportunity ids, NOT list entry ids. Get them from search_opportunities, from the id returned by create_opportunity, or from the entity id of an entry on an opportunity list (search_list_entries, get_single_list_entry); ask if the target is unclear. Args: opportunity_ids: 1-100 opportunity ids per call (ValueError outside that range). Repeated ids are removed before the preview and the deletion. confirm: false returns the summary only; true performs the deletion. Returns: confirm=false: the summary, nothing deleted. confirm=true: {"success", "deleted", "deleted_count", "failed": [{opportunity_id, status_code, error}], "failed_count"}, one attempt per id. Unknown id: 422 (v1 has no 404). List not visible: 403; Restricted Opportunities orgs also need manage permission.
Permanently deletes one or more people (contacts) from Affinity by person id. Confirmation gate: with confirm=false (the default) nothing is deleted. The response is a summary (warning, persons_preview, total, internal_count, related_totals, next_step). Show the whole warning to the user in your own message, unquoted, keeping every person name it shows, the count, and the sentence that says what is kept: a short question alone hides how far the deletion reaches. Call again with confirm=true only after the user approves (or already approved this deletion in the conversation). A user who wants to see what the deletion affects does not need another delete_person call: read it yourself with get_person_list_entries, get_person_info, get_reminders and query_notes. next_step carries the details. WARNING: this deletes the person record itself, not a list membership and not a company link. List entries on all lists, field values, reminders tagged to the person, and notes the person wrote are deleted with them. Person field values on other records that point to them are deleted too. Other notes, files, emails, and meetings are kept but lose the link. There is no undo. Internal people (your coworkers) cannot be deleted; the preview marks the ones it can see and internal_count covers the whole batch. Affinity also refuses people whose email domain belongs to your organization, and no API field reports that, so a confirmed call can still report them in failed with a 422. If the user takes a person off one list and you hold list entry ids, use delete_list_entries; the person stays. To unlink a person from a company use update_person with company_ids=[]. Never call two of these tools for the same person. person_ids are person ids, NOT list entry ids. Get them from search_persons, get_person_info, get_persons_info, the id returned by create_person, or the entity id of an entry on a person list (search_list_entries, get_single_list_entry); ask if the target is unclear. Args: person_ids: 1-100 person ids per call (ValueError outside that range). Repeated ids are removed before the preview and the deletion. confirm: false returns the summary only; true performs the deletion. Returns: confirm=false: the summary, nothing deleted. related_totals is {"list_entries": n, "partial": bool}: how many list entries the batch takes with it, partial=true when the number is a lower bound because an id could not be read. Lists hidden from the API key are never counted and do not set partial: the API filters them out with no signal. confirm=true: {"success", "deleted", "deleted_count", "failed": [{person_id, status_code, error}], "failed_count"}, one attempt per id. Unknown id: 422 (v1 has no 404). Internal or inferred-internal person: 422 "Deleting internal or inferred_internal person is not allowed.". API key without External API access: 403.
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. 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 100, max is 100 Returns: A dict with `data` (list of list entries) and `pagination` with `nextCursor` / `prevCursor` strings (pass directly to the `cursor` param; absent when there is no such page) and `nextUrl` / `prevUrl` full URLs (null when there is no such page).
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 means there are no inferred connections for the company. 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 a page of entities directly attached to a note. For the reverse (finding notes attached to an entity), use get_notes_for_entity. A note may have more attachments than one page holds. To answer questions that depend on every attachment (counts, "is X attached", totals), keep calling with `pagination.nextCursor` until it is absent, or pass total_count=True first to learn how many there are. 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. 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 entities to include in the page. Default is 20, max is 100. total_count: If True, include the total number of attached entities of this type as `pagination.totalCount`. Costs an extra count query, so request it only when the count itself matters. Returns: JSON object with: - data: list of attached entities of the requested type. - pagination: `nextCursor` / `prevCursor` strings (pass directly to the `cursor` param) and `nextUrl` / `prevUrl` full URLs. A missing or null `nextCursor` means this is the last page. `totalCount` is present only when total_count is True.
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. 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 20, maximum is 100. Returns: A dict with `data` (metadata about each dropdown option) and `pagination` with `nextCursor` / `prevCursor` strings (pass directly to the `cursor` param; absent when there is no such page) and `nextUrl` / `prevUrl` full URLs (null when there is no such page).
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. 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 100, max 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: A dict with `data` (field metadata) and `pagination` with `nextCursor` / `prevCursor` strings (pass directly to the `cursor` param; absent when there is no such page) and `nextUrl` / `prevUrl` full URLs (null when there is no such page). 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 metadata for a single entity file (attachment / document) by ID. Use this to hydrate a file found via search_files with details it does not carry, such as who uploaded it and when. search_files only returns id, name, pageNumber, and preview; this fills in uploader_id and created_at (plus any other metadata fields returned for the file). IMPORTANT: - Use search_files (or get_entity_files) first to find a file and obtain its id; this tool does not search. - Use download_entity_file for the file's actual content; this tool only returns metadata. - This endpoint has no 404 path. A file id that is missing, deleted, or owned by another org fails validation upstream and returns a 422 whose message reads "expected <id> to be a valid id for model Affinity::Models::EntityFile". Treat that 422 as "file does not exist or is not accessible": do not retry, and do not treat it as a malformed request. An ACL denial on a file that does exist returns 403. Either way the call fails outright, never with a partial record. Args: file_id: The unique ID of the entity file to look up. Returns: The file's metadata: id, name, size, uploader_id, created_at, the entity ID field (organization_id/person_id/opportunity_id), and entity_type (0 = person, 1 = company, 8 = opportunity) with entity_type_name (the same value decoded to "person"/"company"/"opportunity"). The two entity_type fields are added by this tool, derived from the entity ID field; the API does not return them. Pass entity_type back to any tool taking an entity_type argument, and show entity_type_name to a person. Both are absent when the file carries no recognized entity ID field, so treat them as optional.
Get files attached to a company, person, or opportunity, or every file in the org. Returns metadata only (id, name, size, timestamps); file contents are not downloaded. Pass both entity_id and entity_type to scope the result to one entity. Omit both to get every file in the org, ordered reverse chronologically (newest entity_files.id first). NO SERVER-SIDE UPLOADER OR DATE FILTER: this endpoint does not accept an uploader or date-range filter. For requests like "what files did I add in the last 2 weeks", omit entity_id/entity_type, page through the org-wide results with page_token, and filter the returned entries yourself by uploader_id and created_at. An org-wide call returns ONE page of files. The org may have more files than one page holds. To answer questions that depend on every file (counts, "did I upload X", date-window sweeps), keep calling with "next_page_token" until it is absent. Args: entity_id: The ID of the company, person, or opportunity whose files should be retrieved. Omit together with entity_type for an org-wide, unfiltered list. entity_type: What type of entity the entity_id refers to. Options: 0 = person, 1 = company, 8 = opportunity. Must be provided together with entity_id, or omitted together with it. page_token: Token for the next page of an org-wide call. Pass the value of "next_page_token" from a prior response. Do NOT parse or modify the token string. Ignored when entity_id/entity_type are provided. page_size: Results per page for an org-wide call. Default 100, max 500. Ignored when entity_id/entity_type are provided. Returns: For an entity-scoped call: structured data containing the list of file metadata entries associated with the entity, under "entity_files". Each entry includes id, name, size, uploader_id, created_at, and the entity ID field (organization_id/person_id/opportunity_id), plus entity_type (0 = person, 1 = company, 8 = opportunity) and entity_type_name (the same value decoded to "person"/"company"/"opportunity"). The two entity_type fields are added by this tool, derived from whichever entity ID field the entry carries; the API does not return them. They are the same fact in two forms: entity_type is the numeric code to pass back to any tool taking an entity_type argument (including this one), and entity_type_name is the label to show a person. An entry with no recognized entity ID field carries neither, so treat both as optional. For an org-wide call: one page of the same under "entity_files", ordered reverse chronologically, plus "next_page_token" (pass directly to the page_token param) when more files exist. A missing "next_page_token" means this is the last page.
Get a paginated list of field value changes across the organization. Designed for delta sync: bound the first call with a `changedAt` filter, then store `pagination.nextCursor` and pass it to subsequent calls to retrieve only new changes since the last sync. The scope is the whole organization and a change's `value` can be a text field, so an unfiltered first call sweeps the org's entire edit history at 100 rows a page. Narrow by `changedAt` rather than paging to the start of time, and do not sweep unprompted — report what the first page holds and offer to fetch more. 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; absent when there is no such page) and `nextUrl` / `prevUrl` full URLs (null when there is no such page). 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 means there are no inferred connections for the company. 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. 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 20, maximum is 100. Returns: A dict with `data` (metadata about each dropdown option) and `pagination` with `nextCursor` / `prevCursor` strings (pass directly to the `cursor` param; absent when there is no such page) and `nextUrl` / `prevUrl` full URLs (null when there is no such page).
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. 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 100, max 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: A dict with `data` (field metadata) and `pagination` with `nextCursor` / `prevCursor` strings (pass directly to the `cursor` param; absent when there is no such page) and `nextUrl` / `prevUrl` full URLs (null when there is no such page). 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 a page of lists in the user's organization that they have access to, optionally filtered by name. More lists may exist than one page holds: keep calling with `pagination.nextCursor` until it is absent to see every list. 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. 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 100, max is 100. Returns: A dict with `data` (list metadata such as list name, entity type, creator ID, and owner ID) and `pagination` with `nextCursor` / `prevCursor` strings (pass directly to the `cursor` param; absent when there is no such page) and `nextUrl` / `prevUrl` full URLs (null when there is no such page).
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. 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 to include in the page. Default is 20, max is 100. Returns: A dict with `data` (list of meetings) and `pagination` with `nextCursor` / `prevCursor` strings (pass directly to the `cursor` param; absent when there is no such page) and `nextUrl` / `prevUrl` full URLs (null when there is no such page). If the org has no access to the unified meetings endpoint, returns `{"data": [], "unsupported": "..."}` pointing to get_meetings_for_entity instead of raising.
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. A larger range is automatically clamped to the most recent 365 days (start_time moves forward) and the response carries a `warnings` entry stating the window actually used; call again with an earlier end_time to fetch older meetings. start_time must be strictly before end_time: an inverted or empty range is rejected with an error, not clamped. 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: Token for the next page. Pass the value of "next_page_token" from the previous response; when a response has no "next_page_token", it is the last page. To answer questions that depend on every meeting in the range, keep calling with it until it is absent. Do NOT parse or modify the token string. 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. If the requested date range exceeded 365 days, a `warnings` list describes the clamped window that was used.
Checks whether a company or person merge has finished, by its task id. Use after merge_companies or merge_persons returned with confirm=true: both return a task_id, and this reads GET /v2/tasks/<entity>-merges/{task_id} once. It does not poll. A merge can stay "in-progress" for several minutes while Affinity retries it; read again when the user asks. "failed" is final. IMPORTANT: entity_type must match the tool that produced the task: a company task read as "person" (or the reverse) is a 404, the same as an unknown id. Requires the Manage Duplicates permission and organization admin role, like the merge tools; a token without it gets a 403. Args: task_id (required): The task_id returned by merge_companies or merge_persons, a UUID such as "76251d92-99e1-416f-a3d2-0935435eb672". Anything that is not a UUID raises ValueError before any API call. entity_type (required): "company" for a merge_companies task, "person" for a merge_persons task. Returns: {"task_id", "entity_type", "status", "results_summary"}. status is "in-progress", "success" or "failed"; results_summary is {"total", "inProgress", "success", "failed"} counts for the task (a merge from these tools is a task of one). Errors: 404 "Resource not found" for an unknown id or the wrong entity_type; 403 without the Manage Duplicates permission.
Get a page of notes attached to a specific company, person, or opportunity. For the reverse (finding entities attached to a note), use get_entities_attached_to_note. The entity may have more notes than one page holds. For counts and totals, pass total_count=True rather than fetching rows: note bodies are free text and 20 of them have measured over 100k characters, past the tool-result cap. Do not sweep an entity's whole note history unprompted, by paging OR by raising limit — one limit=100 call is the same runaway as five pages. Report what the first page holds and offer to fetch more. 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 Take the type from the tool that produced the id (search_persons or get_person_info = 0, search_companies_* or get_company_info = 1, search_opportunities = 8). Do not guess: the three id spaces overlap, so a wrong type returns a 404 or another entity's notes. List entry ids are not entity 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: Number of notes to include in the page. Default is 20, max is 100. Response size scales with the note bodies, so prefer the default plus a follow-up page over one large pull. total_count: If True, include the total number of notes on the entity as `pagination.totalCount`. Costs an extra count query, so request it only when the count itself matters. Returns: JSON object with: - data: list of notes. - pagination: `nextCursor` / `prevCursor` strings (pass directly to the `cursor` param) and `nextUrl` / `prevUrl` full URLs. A missing or null `nextCursor` means this is the last page. `totalCount` is present only when total_count is True. 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. 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 100, max is 100 Returns: A dict with `data` (list of list entries) and `pagination` with `nextCursor` / `prevCursor` strings (pass directly to the `cursor` param; absent when there is no such page) and `nextUrl` / `prevUrl` full URLs (null when there is no such page).
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. Returns one page of results. To answer questions that depend on every matching reminder (counts, "is there a reminder for X", overdue sweeps), keep calling with "next_page_token" until it is absent. 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: Token for the next page. Pass the value of "next_page_token" from the previous response. Do NOT parse or modify the token string. Returns: Structured data containing one page of reminders, plus "next_page_token" (pass directly to the page_token param) when more results exist. A missing "next_page_token" means this is the last page.
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; absent when there is no such page) and `nextUrl` / `prevUrl` full URLs (null when there is no such page).
Paginate through all saved views the user has access to for a specific List. Args: list_id (required): ID of the 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 100, maximum is 100. Returns: Data array of saved views (each containing id, name, type, createdAt) plus a pagination object with `nextCursor` / `prevCursor` strings (pass directly to the `cursor` param; absent when there is no such page) and `nextUrl` / `prevUrl` full URLs (null when there is no such page).
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 a page of dialogue fragments (individual speaker turns) from a transcript. Fragments are free text and a long meeting has thousands of them, so a full transcript does not fit one tool result. Do not sweep a whole transcript unprompted — by paging or by raising limit. Report what the first page holds and offer to fetch more. 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. 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. Default is 20, max is 100. Response size scales with how much each speaker said, so prefer the default plus a follow-up page over one large pull. Returns: A dict with `data` (dialogue fragments, each containing speaker, content, and timestamps) and `pagination` with `nextCursor` / `prevCursor` strings (pass directly to the `cursor` param; absent when there is no such page), `nextUrl` / `prevUrl` full URLs (null when there is no such page), and `totalCount`.
Get a page of users (internal team members) in your Affinity organization. More users may exist than one page holds: keep calling with `pagination.nextCursor` until it is absent to see everyone. 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 "[email protected]". 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: 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 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` with `nextCursor` / `prevCursor` strings (pass directly to the `cursor` param; absent when there is no such page) and `nextUrl` / `prevUrl` full URLs (null when there is no such page).
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-09-28, Affinity competes with 4Degrees, 4Degrees - KSA, MadeMarket, 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.