> ## Documentation Index
> Fetch the complete documentation index at: https://docs.groovin.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Create CRM contacts

> Create contacts in your CRM from LinkedIn people, or link them to a contact that already exists. Companies are found or created along the way.

> [!CAUTION]
>
> This writes to your real CRM: it creates contacts and companies. It can also read LinkedIn live and spend email credits. Check who already exists first, with [Check who is already in your CRM](https://docs.groovin.ai/reference/api/lookup-contacts.md).

## What this call does

| | |
| - | - |
| **LinkedIn** | A live profile read unless you pass `profile`. On Attio, a live company page read can still happen. With sync on: live reads and a history import |
| **Your CRM** | **Creates or links** contacts and companies, with lists and notes |
| **Also** | 1 email credit per email found on a new Attio contact. A full batch can take several minutes |
| **Repeating it** | **Not always safe.** If the CRM search fails, or a first batch is still running, a resend can create duplicates. Check who already exists, then resend only the failed people. |

## Good to know

* An HTTP 200 can hold failures: an item failed when its `crm_contact_id` is `null`.
* Groovin looks for an existing contact by LinkedIn URL, then by name. A single same-name contact with no LinkedIn URL gets linked and receives this URL, even if it is a namesake.
* Attio and HubSpot only. A slow batch is normal: do not resend it. Between batches, wait about 30 seconds, or 2 to 5 minutes with sync on.
* Creating this way ignores your per-person creation blocklist.

Your AI assistant reaches the same action through the MCP tool `create_crm_contacts`.


## OpenAPI

````yaml reference/openapi.json POST /crm/contacts
openapi: 3.1.0
info:
  title: Groovin API
  version: 1.0.0
  description: >-
    Connect LinkedIn and your CRM. Every call acts on your real LinkedIn account
    and CRM: there is no test mode.
servers:
  - url: https://api.groovin.ai/functions/v1/api/v1
security:
  - apiKey: []
tags:
  - name: LinkedIn
  - name: CRM contacts and companies
  - name: LinkedIn connections import
paths:
  /crm/contacts:
    post:
      tags:
        - CRM contacts and companies
      summary: Create CRM contacts
      description: >-
        Create contacts in your CRM from LinkedIn people, or link them to a
        contact that already exists. Companies are found or created along the
        way.
      operationId: create_crm_contacts
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                profiles:
                  type: array
                  items:
                    type: object
                    properties:
                      linkedin_profile_id:
                        type: string
                        description: Their LinkedIn profile id.
                      full_name:
                        type: string
                        description: Their full name.
                      slug:
                        type:
                          - string
                          - 'null'
                        description: >-
                          The end of their LinkedIn URL. Send it when you know
                          it: it is the most reliable way to find an existing
                          contact.
                      sales_nav_profile_id:
                        type:
                          - string
                          - 'null'
                        description: Their Sales Navigator profile id.
                      linkedin_profile_url:
                        type:
                          - string
                          - 'null'
                        description: Their LinkedIn profile URL.
                      connected_at:
                        type:
                          - string
                          - 'null'
                        description: >-
                          When they accepted your connection (ISO 8601). If set,
                          the matching CRM fields are filled in.
                      invite_sent_at:
                        type:
                          - string
                          - 'null'
                        description: >-
                          When you invited them (ISO 8601). If set, the matching
                          CRM fields are filled in. A date that cannot be read
                          answers 400.
                      profile:
                        $ref: '#/components/schemas/LinkedinProfile'
                        description: >-
                          The `profile` object from [Get a LinkedIn
                          profile](/reference/api/get-profile), unchanged. It
                          saves one live LinkedIn request per person.
                    required:
                      - linkedin_profile_id
                      - full_name
                  description: The people to create, up to 25 per call.
                org_linkedin_account_id:
                  type: integer
                  description: >-
                    Leave this out to use your own LinkedIn account. Another
                    person's account is refused with a 403.
                sync_messages:
                  type: boolean
                  description: >-
                    `true` turns on message sync and imports the chat history.
                    `false` keeps it off. Leave it out to follow your Groovin
                    preference.
              required:
                - profiles
            examples:
              placeholder:
                summary: Replace every value
                value:
                  profiles:
                    - linkedin_profile_id: REPLACE_WITH_LINKEDIN_PROFILE_ID
                      full_name: REPLACE_WITH_FULL_NAME
                  sync_messages: false
      responses:
        '200':
          description: Success.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/CreateCrmContactsResult'
              example:
                org_linkedin_account_id: 4821
                total: 1
                succeeded: 1
                failed: 0
                summary: 1 created.
                results:
                  - linkedin_profile_id: ACoAAExampleLinkedinProfileId
                    crm_contact_id: example-crm-contact-id
                    outcome: created_new_contact
                    email: null
                    sync_enabled: false
                    history_import_triggered: false
                    error: null
                    error_status: null
                    matched_crm_contact_ids: null
                retry_guidance: null
        '400':
          description: 'Something in the request is wrong: `error` says what.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '401':
          description: The API key is missing, wrong or revoked.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '403':
          description: >-
            Your account cannot do this: no CRM connected, inactive
            subscription, Billing admin role, or a teammate's LinkedIn account.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '404':
          description: >-
            What you asked for does not exist, or your LinkedIn account is not
            connected.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '429':
          description: LinkedIn or your CRM is slowing you down. Wait at least 5 minutes.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '500':
          description: Something failed on our side.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '502':
          description: >-
            LinkedIn or your CRM could not be reached or gave an unreadable
            answer.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
components:
  schemas:
    LinkedinProfile:
      type:
        - object
        - 'null'
      properties:
        id:
          type:
            - string
            - 'null'
          description: LinkedIn profile id (ACoAA…).
        url:
          type:
            - string
            - 'null'
          description: Canonical profile URL.
        slugs:
          type:
            - array
            - 'null'
          items:
            type: string
          description: All known slugs / ids for the profile.
        salesNavId:
          type:
            - string
            - 'null'
          description: Sales Navigator profile id (ACwAA…/ADEAA…), when known.
        firstName:
          type:
            - string
            - 'null'
          description: First name.
        lastName:
          type:
            - string
            - 'null'
          description: Last name.
        emailAddress:
          type:
            - string
            - 'null'
          description: LinkedIn-registered personal email, when visible.
        headline:
          type:
            - string
            - 'null'
          description: LinkedIn headline.
        summary:
          type:
            - string
            - 'null'
          description: Long-form bio.
        locationName:
          type:
            - string
            - 'null'
          description: Flat location string, e.g. "Paris, Île-de-France, France".
        location:
          type:
            - object
            - 'null'
          properties:
            city:
              type:
                - string
                - 'null'
              description: City part of the structured location.
            region:
              type:
                - string
                - 'null'
              description: Region/state part of the structured location.
            country:
              type:
                - string
                - 'null'
              description: Country name of the structured location.
            countryCode:
              type:
                - string
                - 'null'
              description: ISO-2 country code.
          description: Structured location.
        industryName:
          type:
            - string
            - 'null'
          description: Industry, read from the profile's ongoing positions.
        educationSchoolNames:
          type:
            - array
            - 'null'
          items:
            type: string
          description: All school names.
        phoneNumbers:
          type:
            - array
            - 'null'
          items:
            type: string
          description: Phone numbers LinkedIn exposes, when visible.
        websites:
          type:
            - array
            - 'null'
          items:
            type: string
          description: Websites listed on the profile.
        currentPosition:
          type:
            - object
            - 'null'
          properties:
            jobTitle:
              type:
                - string
                - 'null'
              description: Job title of the current position.
            companyName:
              type:
                - string
                - 'null'
              description: Company name of the current position.
            companyId:
              type:
                - string
                - 'null'
              description: LinkedIn company id.
            companyUrl:
              type:
                - string
                - 'null'
              description: LinkedIn URL of that company.
            companyLogoUrl:
              type:
                - string
                - 'null'
              description: Company logo URL.
            jobDescription:
              type:
                - string
                - 'null'
              description: >-
                The person's own description of THIS role, from their LinkedIn
                experience entry.
            jobLocationName:
              type:
                - string
                - 'null'
              description: >-
                Where the person holds THIS role, as a flat string (may differ
                from the company HQ).
            companyDescription:
              type:
                - string
                - 'null'
              description: >-
                The COMPANY's own description, from its LinkedIn company page —
                not the person's description of their role (that is
                `jobDescription`).
            companyLocationName:
              type:
                - string
                - 'null'
              description: >-
                The COMPANY's headquarters location as a flat string — not where
                the person holds the role (that is `jobLocationName`).
            industries:
              type:
                - array
                - 'null'
              items:
                type: string
              description: Industries of the company.
            startDate:
              type:
                - string
                - 'null'
              description: YYYY-MM-DD.
            companySizeRange:
              type:
                - string
                - 'null'
              description: Employee-count range, e.g. "11-50".
            companyDomain:
              type:
                - string
                - 'null'
              description: Company domain — used to find or create the CRM company.
            companyWebsiteUrl:
              type:
                - string
                - 'null'
              description: Company website URL.
            companyFoundedYear:
              type:
                - number
                - 'null'
              description: Year the company was founded.
            companyLocationCountryCode:
              type:
                - string
                - 'null'
              description: ISO-2 country code of the company HQ.
            specialties:
              type:
                - array
                - 'null'
              items:
                type: string
              description: Company specialties.
          description: The single current position, with its snapshot of the company.
      description: >-
        A LinkedIn profile, as Groovin models it. Field names use camelCase
        here, unlike the rest of the API. Every field is optional.
    CreateCrmContactsResult:
      type: object
      description: >-
        The result of a batch. HTTP 200 does not mean every person succeeded:
        check each item.
      required:
        - org_linkedin_account_id
        - total
        - succeeded
        - failed
        - summary
        - results
        - retry_guidance
      properties:
        org_linkedin_account_id:
          type: integer
          description: Groovin's id for the LinkedIn account this answer is about.
        total:
          type: integer
          description: People in the batch.
        succeeded:
          type: integer
          description: People who now have a CRM contact.
        failed:
          type: integer
          description: People who do not.
        summary:
          type: string
          description: A one-line summary of the batch, worth reading.
        results:
          type: array
          items:
            $ref: '#/components/schemas/CreateCrmContactItemResult'
          description: One item per person.
        retry_guidance:
          type:
            - string
            - 'null'
          description: >-
            Plain-text advice when some items failed for a temporary reason,
            otherwise `null`. Follow it: it says when to retry, and which items.
    Error:
      type: object
      description: >-
        What every failed request answers, with an HTTP status. The text can
        change: decide what to do from the status, and show the text to a
        person.
      required:
        - error
      properties:
        error:
          type: string
          description: What went wrong, and sometimes what to do next.
    CreateCrmContactItemResult:
      type: object
      description: >-
        What happened for one person. A `null` `crm_contact_id` means this item
        failed.
      required:
        - linkedin_profile_id
        - crm_contact_id
        - outcome
        - email
        - sync_enabled
        - history_import_triggered
        - error
        - error_status
        - matched_crm_contact_ids
      properties:
        linkedin_profile_id:
          type: string
          description: LinkedIn profile id, the `ACoAA…` identifier of a person.
        crm_contact_id:
          type:
            - string
            - 'null'
          description: The CRM contact, or `null` on failure.
        outcome:
          type:
            - string
            - 'null'
          enum:
            - wrong_crm
            - no_crm_user
            - linkedin_account_not_connected
            - preferences_disabled
            - profile_blocked
            - missing_name
            - linkedin_profile_unavailable
            - multiple_url_matches
            - multiple_name_matches
            - multiple_email_matches
            - uniqueness_conflict
            - paired_existing_contact
            - created_new_contact
            - used_existing_contact
            - no_contact
            - null
          description: >-
            What the creation did. `multiple_*_matches` and
            `uniqueness_conflict` mean the person probably already exists:
            review the records in `matched_crm_contact_ids`.
        email:
          type:
            - string
            - 'null'
          description: >-
            A verified work email found and saved on a newly created Attio
            contact. Finding one spends an email credit. `null` otherwise.
        sync_enabled:
          type: boolean
          description: '`true` when message sync was turned on.'
        history_import_triggered:
          type: boolean
          description: '`true` when this call started importing the chat history.'
        error:
          type:
            - string
            - 'null'
          description: >-
            What went wrong for this item, or `null`. Always read it: the rest
            of the batch can still have succeeded.
        error_status:
          type:
            - integer
            - 'null'
          description: >-
            HTTP status behind `error`. `429` means rate limited and `5xx`
            usually means temporary: retry later. `null` when there is no error.
        matched_crm_contact_ids:
          type:
            - array
            - 'null'
          items:
            type: string
          description: >-
            Existing records to review, with `multiple_*_matches` or
            `uniqueness_conflict`. `null` otherwise.
  securitySchemes:
    apiKey:
      type: http
      scheme: bearer
      description: >-
        Your Groovin API key (`gv_live_…`), created in the extension under
        Settings, MCP and API.

````

This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.