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

# Workflow Recipes

> Step-by-step API workflows for common recruiting operations.

These recipes show how to chain API calls for complete recruiting workflows. Each recipe includes the exact endpoints, request bodies, and expected responses.

## Manage project pipeline entries

Use this workflow when an integration needs to read candidates from a project,
filter them by stage, add new leads to a project, or move existing leads through
the pipeline.

<Steps>
  <Step title="Get the project stages">
    Pipeline entry filters use **stage IDs**, not stage names. Start by reading
    the project and storing the stage ID you need, for example the `Sourced`
    stage.

    ```bash theme={null}
    curl -X GET "https://app.leonar.app/api/v1/projects/project-uuid" \
      -H "Authorization: Bearer leo_your_api_key"
    ```

    Response excerpt:

    ```json theme={null}
    {
      "data": {
        "id": "project-uuid",
        "name": "Senior Frontend Engineers",
        "pipeline": {
          "stages": [
            {
              "id": "sourced-stage-uuid",
              "name": "Sourced",
              "position": 0,
              "system_category": "sourced",
              "entries_count": 12
            }
          ]
        }
      }
    }
    ```
  </Step>

  <Step title="List only entries in a given stage">
    The project is selected by the URL path (`/projects/{project_id}/entries`).
    Add `stage_id` as a query parameter to return only entries currently in that
    stage for the same project. Values like `Sourced` are not accepted directly;
    use the stage UUID returned by `GET /projects/{id}`.

    ```bash theme={null}
    curl -X GET "https://app.leonar.app/api/v1/projects/project-uuid/entries?stage_id=sourced-stage-uuid&limit=100&offset=0" \
      -H "Authorization: Bearer leo_your_api_key"
    ```
  </Step>

  <Step title="Create or update the contact">
    If the lead is new, create a contact first:

    ```bash theme={null}
    curl -X POST "https://app.leonar.app/api/v1/contacts" \
      -H "Authorization: Bearer leo_your_api_key" \
      -H "Content-Type: application/json" \
      -d '{
        "first_name": "Sophie",
        "last_name": "Martin",
        "title": "Senior Software Engineer",
        "current_company": "Doctolib",
        "linkedin_profile": "https://www.linkedin.com/in/sophie-martin"
      }'
    ```

    If the lead already exists and you only need to update their profile, use:

    ```bash theme={null}
    curl -X PUT "https://app.leonar.app/api/v1/contacts/contact-uuid" \
      -H "Authorization: Bearer leo_your_api_key" \
      -H "Content-Type: application/json" \
      -d '{
        "title": "Staff Software Engineer",
        "current_company": "Doctolib"
      }'
    ```
  </Step>

  <Step title="Add the contact to the project">
    Add the contact to the project pipeline. If `stage_id` is omitted, Leonar
    adds the contact to the first stage of the project pipeline. Per-contact
    sequence values can be supplied under `custom_variables`. Project variables
    such as `project_variable_1` are shared by every entry in the project and
    must instead be set through `PUT /projects/{id}` under `project_variables`.

    ```bash theme={null}
    curl -X POST "https://app.leonar.app/api/v1/projects/project-uuid/entries" \
      -H "Authorization: Bearer leo_your_api_key" \
      -H "Content-Type: application/json" \
      -d '{
        "contact_id": "contact-uuid",
        "stage_id": "sourced-stage-uuid",
        "custom_variables": {
          "custom_variable_1": "Per-contact value"
        }
      }'
    ```
  </Step>

  <Step title="Move an existing project lead to another stage">
    Use the pipeline entry ID from `GET /projects/{id}/entries`, then pass the
    target stage UUID.

    ```bash theme={null}
    curl -X PATCH "https://app.leonar.app/api/v1/pipeline-entries/pipeline-entry-uuid" \
      -H "Authorization: Bearer leo_your_api_key" \
      -H "Content-Type: application/json" \
      -d '{
        "stage_id": "target-stage-uuid"
      }'
    ```
  </Step>
</Steps>

***

## Source candidates and add to project

Find candidates on LinkedIn and add them to a recruiting project's pipeline.

<Steps>
  <Step title="Get your connected LinkedIn account">
    ```bash theme={null}
    curl -X GET "https://app.leonar.app/api/v1/connected-accounts" \
      -H "Authorization: Bearer leo_your_api_key"
    ```

    Response — pick an account with `api_status.recruiter: "active"` or `api_status.sales_navigator: "active"`:

    ```json theme={null}
    {
      "data": [
        {
          "id": "acc-uuid-here",
          "name": "Alice Recruiter",
          "license_type": "recruiter",
          "api_status": {
            "classic": "active",
            "sales_navigator": "inactive",
            "recruiter": "active"
          }
        }
      ]
    }
    ```
  </Step>

  <Step title="Look up location IDs (for LinkedIn filters)">
    ```bash theme={null}
    curl -X GET "https://app.leonar.app/api/v1/sourcing/linkedin/locations?q=Paris&account_id=acc-uuid-here&api_type=recruiter" \
      -H "Authorization: Bearer leo_your_api_key"
    ```

    Response:

    ```json theme={null}
    {
      "data": [
        {"id": "105015875", "title": "Paris, Ile-de-France, France"},
        {"id": "104482823", "title": "Paris Area, France"}
      ]
    }
    ```
  </Step>

  <Step title="Search LinkedIn profiles">
    <Warning>
      LinkedIn pagination is cursor-based. To load more results, reuse the `cursor` returned by the previous response. Do not send only `page: 2`; that can return the same profiles as page 1.
    </Warning>

    ```bash theme={null}
    curl -X POST "https://app.leonar.app/api/v1/sourcing/linkedin/search" \
      -H "Authorization: Bearer leo_your_api_key" \
      -H "Content-Type: application/json" \
      -d '{
        "project_id": "project-uuid",
        "account_id": "acc-uuid-here",
        "job_titles": ["Software Engineer", "Backend Developer"],
        "location_ids": {"105015875": "Paris, France"},
        "years_experience": {"min": 3, "max": 10},
        "page": 1,
        "page_size": 25
      }'
    ```

    Response — profiles with `already_in_project: false` can be added:

    ```json theme={null}
    {
      "data": {
        "profiles": [
          {
            "profile_id": "urn:li:member:123456",
            "first_name": "Sophie",
            "last_name": "Martin",
            "headline": "Senior Software Engineer at Doctolib",
            "current_job": {"title": "Senior Software Engineer", "company_name": "Doctolib"},
            "already_in_project": false,
            "existing_contact_id": null
          }
        ],
        "total_count": 342,
        "filtered_count": 340,
        "has_more": true,
        "next_page": 2,
        "cursor": "eyJzdGFydCI6MjV9"
      }
    }
    ```
  </Step>

  <Step title="Load the next LinkedIn page">
    Send the same search body with the returned `cursor`. The `cursor` is the value that advances the LinkedIn result set.

    ```bash theme={null}
    curl -X POST "https://app.leonar.app/api/v1/sourcing/linkedin/search" \
      -H "Authorization: Bearer leo_your_api_key" \
      -H "Content-Type: application/json" \
      -d '{
        "project_id": "project-uuid",
        "account_id": "acc-uuid-here",
        "job_titles": ["Software Engineer", "Backend Developer"],
        "location_ids": {"105015875": "Paris, France"},
        "years_experience": {"min": 3, "max": 10},
        "cursor": "eyJzdGFydCI6MjV9",
        "page_size": 25
      }'
    ```
  </Step>

  <Step title="Add selected profiles to project">
    Pass the **full profile objects** directly from the search response. Include `experiences`, `educations`, `skills`, `summary`, and `picture_url` to create rich contact records:

    ```bash theme={null}
    curl -X POST "https://app.leonar.app/api/v1/sourcing/add-to-project" \
      -H "Authorization: Bearer leo_your_api_key" \
      -H "Content-Type: application/json" \
      -d '{
        "project_id": "project-uuid",
        "profiles": [
          {
            "profile_id": "urn:li:member:123456",
            "first_name": "Sophie",
            "last_name": "Martin",
            "headline": "Senior Software Engineer at Doctolib",
            "picture_url": "https://media.licdn.com/dms/image/example.jpg",
            "location": "Paris, Île-de-France, France",
            "linkedin_url": "https://www.linkedin.com/in/sophie-martin",
            "summary": "Experienced engineer with 8 years in full-stack development...",
            "current_job": {
              "title": "Senior Software Engineer",
              "company_name": "Doctolib"
            },
            "experiences": [
              {
                "title": "Senior Software Engineer",
                "company_name": "Doctolib",
                "start_date": "2021-03-01",
                "end_date": null,
                "description": "Leading backend team, Python/Django stack...",
                "is_current": true
              },
              {
                "title": "Software Engineer",
                "company_name": "Criteo",
                "start_date": "2018-01-01",
                "end_date": "2021-02-28",
                "description": "Built real-time bidding systems...",
                "is_current": false
              }
            ],
            "educations": [
              {
                "educational_establishment": "École Polytechnique",
                "diploma": "Master of Engineering",
                "specialization": "Computer Science",
                "start_date": "2014-09-01",
                "end_date": "2018-06-30"
              }
            ],
            "skills": ["Python", "Django", "TypeScript", "PostgreSQL"],
            "total_years_experience": 8.2
          }
        ]
      }'
    ```

    <Info>
      The more fields you include, the richer the contact record. Fields like `already_in_project` and `existing_contact_id` from the search response are ignored — they are response-only metadata.
    </Info>

    Response:

    ```json theme={null}
    {
      "data": {
        "added": 1,
        "skipped": 0,
        "contact_ids": ["new-contact-uuid"],
        "skipped_profiles": [],
        "stage": {"id": "stage-uuid", "name": "Sourced", "category": "sourced"}
      }
    }
    ```
  </Step>
</Steps>

***

## Enrich a contact and find their email

<Steps>
  <Step title="Trigger enrichment">
    ```bash theme={null}
    curl -X POST "https://app.leonar.app/api/v1/contacts/contact-uuid/enrich" \
      -H "Authorization: Bearer leo_your_api_key" \
      -H "Content-Type: application/json" \
      -d '{"type": "work_email"}'
    ```

    Response:

    ```json theme={null}
    {
      "data": {
        "request_id": "enrich-request-uuid",
        "type": "work_email",
        "status": "pending"
      }
    }
    ```
  </Step>

  <Step title="Poll until completed">
    Enrichment is asynchronous. Poll every 5 seconds:

    ```bash theme={null}
    curl -X GET "https://app.leonar.app/api/v1/enrichment/enrich-request-uuid" \
      -H "Authorization: Bearer leo_your_api_key"
    ```

    Response when completed:

    ```json theme={null}
    {
      "data": {
        "id": "enrich-request-uuid",
        "contact_id": "contact-uuid",
        "enrichment_type": "work_email",
        "status": "completed",
        "result": {
          "email": "sophie@doctolib.com",
          "confidence": 0.95
        },
        "created_at": "2025-02-10T10:00:00Z",
        "completed_at": "2025-02-10T10:00:08Z"
      }
    }
    ```

    <Info>
      The email is automatically added to the contact record. No need to update the contact manually.
    </Info>
  </Step>
</Steps>

***

## Enroll contacts in a sequence

<Steps>
  <Step title="List available sequences">
    ```bash theme={null}
    curl -X GET "https://app.leonar.app/api/v1/sequences?status=active&limit=10" \
      -H "Authorization: Bearer leo_your_api_key"
    ```
  </Step>

  <Step title="Enroll contacts (simple format)">
    ```bash theme={null}
    curl -X POST "https://app.leonar.app/api/v1/sequences/sequence-uuid/enroll" \
      -H "Authorization: Bearer leo_your_api_key" \
      -H "Content-Type: application/json" \
      -d '{
        "contact_ids": [
          "contact-uuid-1",
          "contact-uuid-2",
          "contact-uuid-3"
        ]
      }'
    ```
  </Step>

  <Step title="Or enroll with custom variables">
    Sequence custom variables use fixed placeholder names:
    `{{custom_variable_1}}` through `{{custom_variable_5}}`. Add those
    placeholders in the sequence step content, then pass per-contact values in
    `custom_variables` when enrolling. Arbitrary names like `project_name` are
    rejected.

    Example sequence step:

    ```text theme={null}
    Hi {{first_name}}, I thought the {{custom_variable_1}} role could be a fit because of {{custom_variable_2}}.
    ```

    Example enrollment payload:

    ```bash theme={null}
    curl -X POST "https://app.leonar.app/api/v1/sequences/sequence-uuid/enroll" \
      -H "Authorization: Bearer leo_your_api_key" \
      -H "Content-Type: application/json" \
      -d '{
        "contacts": [
          {
            "contact_id": "contact-uuid-1",
            "custom_variables": {
              "custom_variable_1": "Senior Frontend Engineer",
              "custom_variable_2": "your React component library work"
            }
          }
        ]
      }'
    ```

    At send time, Leonar resolves the example step to something like:

    ```text theme={null}
    Hi Ada, I thought the Senior Frontend Engineer role could be a fit because of your React component library work.
    ```

    Response:

    ```json theme={null}
    {
      "data": {
        "enrolled": 1,
        "skipped_blacklisted": 0,
        "skipped_already_enrolled": 0,
        "skipped_active_elsewhere": 0,
        "skipped_not_found": 0
      }
    }
    ```
  </Step>
</Steps>

***

## Create and manage a deal

<Steps>
  <Step title="Get pipeline stages">
    ```bash theme={null}
    curl -X GET "https://app.leonar.app/api/v1/deal-pipelines" \
      -H "Authorization: Bearer leo_your_api_key"
    ```
  </Step>

  <Step title="Create a deal">
    ```bash theme={null}
    curl -X POST "https://app.leonar.app/api/v1/deals" \
      -H "Authorization: Bearer leo_your_api_key" \
      -H "Content-Type: application/json" \
      -d '{
        "title": "Recruitment - 3 Senior Engineers",
        "company_id": "company-uuid",
        "pipeline_id": "pipeline-uuid",
        "amount": 45000,
        "currency": "EUR",
        "expected_close_date": "2025-03-15"
      }'
    ```
  </Step>

  <Step title="Link a contact to the deal">
    ```bash theme={null}
    curl -X POST "https://app.leonar.app/api/v1/deals/deal-uuid/contacts" \
      -H "Authorization: Bearer leo_your_api_key" \
      -H "Content-Type: application/json" \
      -d '{"contact_id": "contact-uuid"}'
    ```
  </Step>

  <Step title="Move deal through stages">
    ```bash theme={null}
    curl -X PUT "https://app.leonar.app/api/v1/deals/deal-uuid/stage" \
      -H "Authorization: Bearer leo_your_api_key" \
      -H "Content-Type: application/json" \
      -d '{"stage_id": "next-stage-uuid"}'
    ```
  </Step>

  <Step title="Close the deal">
    ```bash theme={null}
    curl -X POST "https://app.leonar.app/api/v1/deals/deal-uuid/close" \
      -H "Authorization: Bearer leo_your_api_key" \
      -H "Content-Type: application/json" \
      -d '{
        "status": "won",
        "signed_amount": 45000,
        "actual_close_date": "2025-03-10"
      }'
    ```
  </Step>
</Steps>

***

## Search and tag contacts

<Steps>
  <Step title="Create a tag">
    ```bash theme={null}
    curl -X POST "https://app.leonar.app/api/v1/tags" \
      -H "Authorization: Bearer leo_your_api_key" \
      -H "Content-Type: application/json" \
      -d '{"name": "ai-sourced", "color": "#6366F1", "scope": "contact"}'
    ```
  </Step>

  <Step title="Search contacts">
    ```bash theme={null}
    curl -X POST "https://app.leonar.app/api/v1/contacts/search" \
      -H "Authorization: Bearer leo_your_api_key" \
      -H "Content-Type: application/json" \
      -d '{
        "query": "react typescript",
        "location": "Paris",
        "limit": 50
      }'
    ```
  </Step>

  <Step title="Tag matching contacts">
    For each matching contact:

    ```bash theme={null}
    curl -X POST "https://app.leonar.app/api/v1/contacts/contact-uuid/tags" \
      -H "Authorization: Bearer leo_your_api_key" \
      -H "Content-Type: application/json" \
      -d '{"tag_id": "tag-uuid"}'
    ```
  </Step>
</Steps>

***

## Send a message to a contact

```bash theme={null}
curl -X POST "https://app.leonar.app/api/v1/messages" \
  -H "Authorization: Bearer leo_your_api_key" \
  -H "Content-Type: application/json" \
  -d '{
    "contact_id": "contact-uuid",
    "channel": "email",
    "subject": "Exciting opportunity at Doctolib",
    "content": "Hi Sophie, I came across your profile..."
  }'
```

For LinkedIn messages, specify the channel and optionally the sender account:

```bash theme={null}
curl -X POST "https://app.leonar.app/api/v1/messages" \
  -H "Authorization: Bearer leo_your_api_key" \
  -H "Content-Type: application/json" \
  -d '{
    "contact_id": "contact-uuid",
    "channel": "linkedin",
    "content": "Hi Sophie, I noticed your experience with React...",
    "sender_account_id": "account-uuid"
  }'
```

For first-time WhatsApp messages, provide an international phone number when the contact does not have an existing WhatsApp chat or provider ID:

```bash theme={null}
curl -X POST "https://app.leonar.app/api/v1/messages" \
  -H "Authorization: Bearer leo_your_api_key" \
  -H "Content-Type: application/json" \
  -d '{
    "contact_id": "contact-uuid",
    "channel": "whatsapp",
    "content": "Hi Sophie, I wanted to reach out on WhatsApp...",
    "recipient_phone": "+33612345678"
  }'
```
