# Contacts

Manage your CRM contacts (leads). Search, create, update, and organize contacts across lists and pipeline stages.

**Mass action types** for `PUT /leads/api/leads/mass-action`: `contact_add_tags`, `contact_remove_tags`, `contact_replace_tags`, `contact_change_list`, `contact_change_pipeline_stage`, `contact_change_custom_field`, `contact_add_to_flow`, `contact_cancel_from_flows`, `contact_delete`, `contact_export_csv`, `contact_mark_read`.


## Find a contact

 - [POST /leads/api/leads/lookup-one](https://api.grinfi.io/openapi/contacts/findonecontact.md): Look up a single contact by LinkedIn ID, email address, or name + company combination. At least one identifier must be provided. This is useful for checking if a contact already exists before creating a new one.

## Search contacts

 - [POST /leads/api/leads/search](https://api.grinfi.io/openapi/contacts/searchcontacts.md): Search contacts using advanced filters with pagination and sorting. Use this to find contacts by any combination of fields — name, company, email status, pipeline stage, tags, and more.

## Get a contact

 - [GET /leads/api/leads/{uuid}](https://api.grinfi.io/openapi/contacts/getlead.md): Retrieve full details of a contact by their UUID.

## Update a contact

 - [PUT /leads/api/leads/{uuid}](https://api.grinfi.io/openapi/contacts/updatelead.md): Update one or more fields on an existing contact. Only include the fields you want to change.

Important: this endpoint only accepts the standard contact fields listed below. Custom field values are silently ignored. To set custom fields use POST /leads/api/custom-field-values. Alternatively, the Upsert endpoint supports custom_fields in a single request.

## Delete a contact

 - [DELETE /leads/api/leads/{uuid}](https://api.grinfi.io/openapi/contacts/deletelead.md): Permanently delete a contact by UUID. This creates a background deletion job.

## Create or update a contact

 - [POST /leads/api/leads/upsert](https://api.grinfi.io/openapi/contacts/upsertcontact.md): Create a new contact in a list, or update an existing one if found. The contact is identified by linkedin_id. If the contact already exists and update_if_exists is true, their data will be updated. Optionally move the contact to the specified list with move_to_list.

Tip: Unlike PUT /leads/api/leads/{uuid}, this endpoint accepts custom_fields so you can set standard and custom fields in a single request.

## Mass action on contacts

 - [PUT /leads/api/leads/mass-action](https://api.grinfi.io/openapi/contacts/leadsmassaction.md): Perform a bulk action on contacts matching a filter.

Supported action types:
| Type | Payload | Description | |------|---------|-------------| | contact_add_tags | {tag_uuid} | Add a tag | | contact_remove_tags | {tag_uuid} | Remove a tag | | contact_replace_tags | {tag_uuids: [...]} | Replace all tags | | contact_change_list | {list_uuid} | Move to a different list | | contact_change_pipeline_stage | {pipeline_stage_uuid} | Change pipeline stage | | contact_change_custom_field | {custom_field_uuid, value} | Set custom field value | | contact_add_to_flow | {flow_uuid} | Add to automation | | contact_cancel_from_flows | {flow_uuids: [...]} | Cancel from automations | | contact_delete | — | Delete contacts | | contact_export_csv | — | Export to CSV | | contact_mark_read | — | Mark conversations as read |

## Count contacts

 - [POST /leads/api/leads/count](https://api.grinfi.io/openapi/contacts/countcontacts.md): Count contacts matching a filter without returning the full data. Use all: true to count every contact in the team, or pass an explicit list of contact UUIDs in ids (with all: false) to count a specific subset.

## Get team-wide engagement metrics

 - [POST /leads/api/leads/metrics](https://api.grinfi.io/openapi/contacts/getleadmetrics.md): Returns aggregated outreach metrics across the whole team for the requested period.

Important: despite accepting a filter field, this endpoint currently returns team-wide totals only — filter.sender_profile_uuid and other keys are silently ignored. The group_by field is also accepted but does not produce a per-group breakdown in the response. Plan for team-level reporting only.

For per-sender activity counts (without period filter), use: - POST /flows/api/linkedin-messages/group-counts with filter.sender_profile_uuid - GET /flows/api/tasks?filtersender_profile_uuid=&filterstatus=

## Run advanced enrichment on contacts

 - [PUT /leads/api/leads/advanced-enrichment](https://api.grinfi.io/openapi/contacts/enrichleadsadvanced.md): Enqueue advanced LinkedIn enrichment on a set of contacts. Enrichment consumes credits — returns 402 Payment Required if your team has insufficient balance.

## List enrichment queue

 - [GET /leads/api/enrichment-queue](https://api.grinfi.io/openapi/contacts/listenrichmentqueue.md): List entries in the enrichment processing queue.

## Get enrichment usage metrics

 - [POST /leads/api/enrichment-queue/metrics](https://api.grinfi.io/openapi/contacts/getenrichmentmetrics.md): Return aggregate metrics about enrichment activity for the team — currently the count of enrichment jobs queued/processed in the current calendar month.

