Skip to main content
This guide explains how to connect an AI agent (Claude, GPT, LangChain, CrewAI, etc.) to the Leonar API so it can search candidates, manage contacts, enroll in sequences, and track deals on behalf of your users.

How it works

The Leonar API is a standard REST API with an OpenAPI 3.0 spec. AI agent frameworks consume this spec to generate tool definitions automatically.
Your Agent  →  OpenAPI Spec  →  Tool Definitions  →  Leonar API
No MCP server or custom SDK is needed. The OpenAPI spec at openapi.yaml is the single source of truth.

Setup

1. Create a scoped API key

Go to Settings > API and create a key with only the scopes your agent needs.
Agent use caseRecommended scopes
Read-only researchcontacts:read, projects:read, companies:read
Sourcing automationsourcing:read, sourcing:write, contacts:read, projects:read
Outreach automationsequences:read, sequences:write, contacts:read, messaging:write
Full CRM agentread_only + contacts:write, projects:write, deals:write
Never give an agent admin scope. Always use the minimum scopes required.

2. Load the OpenAPI spec

import anthropic
import yaml

# Load spec and convert operations to tools
with open("openapi.yaml") as f:
    spec = yaml.safe_load(f)

# Claude's tool_use format accepts OpenAPI-style schemas directly
tools = []
for path, methods in spec["paths"].items():
    for method, operation in methods.items():
        if isinstance(operation, dict) and "operationId" in operation:
            tools.append({
                "name": operation["operationId"],
                "description": operation.get("description", operation.get("summary", "")),
                "input_schema": extract_schema(operation),  # your conversion logic
            })

3. Set the base URL and auth header

Base URL: https://app.leonar.app/api/v1
Authorization: Bearer leo_your_api_key
Content-Type: application/json

Rate limiting strategy

The API allows 1000 requests per hour. For autonomous agents:
  • Check headers: Every response includes X-RateLimit-Remaining and X-RateLimit-Reset
  • Batch operations: Use bulk endpoints (e.g., enroll up to 500 contacts at once) instead of individual calls
  • Exponential backoff: On 429 responses, wait 2^attempt seconds before retrying
  • Pagination: Default limit=50. Use offset to paginate. Don’t fetch all pages unless needed.
import time

def api_call_with_retry(url, **kwargs):
    for attempt in range(5):
        response = requests.request(url=url, **kwargs)
        if response.status_code == 429:
            wait = 2 ** attempt
            time.sleep(wait)
            continue
        return response
    raise Exception("Rate limit exceeded after 5 retries")

Common agent workflows

Workflow 1: Source and add candidates to a project

1. GET /projects                      → Pick target project
2. GET /connected-accounts            → Get LinkedIn account ID
3. GET /sourcing/linkedin/locations   → Resolve location names to IDs
4. POST /sourcing/linkedin/search     → Search with filters
5. POST /sourcing/add-to-project      → Add selected profiles
location_ids requires LinkedIn geo IDs, not plain-text names. You must call GET /sourcing/linkedin/locations?q=Paris&account_id=ACCOUNT_ID first to resolve a location name to its LinkedIn numeric ID. Passing plain text like {"Paris": "Paris"} will silently return zero results.
curl -X GET "https://app.leonar.app/api/v1/projects?status=active&limit=10" \
  -H "Authorization: Bearer leo_your_api_key"
LinkedIn Classic search may return null first_name and last_name. Set resolve_names: true and provide account_id in the add-to-project request to automatically resolve missing names via LinkedIn profile lookup. Without this, contacts are created with “Unknown” as their first name.

Workflow 2: Enrich and enroll in sequence

1. POST /contacts/search     → Find contacts matching criteria
2. POST /contacts/{id}/enrich → Find email (async)
3. GET  /enrichment/{id}      → Poll until completed
4. GET  /sequences            → Pick target sequence
5. POST /sequences/{id}/enroll → Enroll contacts

Workflow 3: Deal pipeline management

1. GET  /deal-pipelines       → Get pipeline stages
2. POST /deals                → Create deal
3. POST /deals/{id}/contacts  → Link contacts to deal
4. PUT  /deals/{id}/stage     → Move through stages
5. POST /deals/{id}/close     → Close as won/lost

Error handling for agents

Your agent should handle these error patterns:
HTTP codeError codeAgent action
400validation_errorFix the request parameters and retry
401invalid_api_keyStop — API key is invalid or revoked
401insufficient_scopeStop — need a key with more scopes
403billing_requiredStop — workspace needs an active subscription
403plan_upgrade_requiredStop — feature not available on current plan
404not_foundThe resource ID is wrong — verify and retry
429rate_limit_exceededWait and retry with exponential backoff
500internal_errorRetry once, then stop and report
def handle_response(response):
    if response.ok:
        return response.json()

    error = response.json().get("error", {})
    code = error.get("code", "unknown")

    if code == "rate_limit_exceeded":
        return "RETRY"
    elif code in ("invalid_api_key", "insufficient_scope", "billing_required"):
        return f"STOP: {error['message']}"
    elif code == "validation_error":
        return f"FIX_INPUT: {error['message']}"
    elif code == "not_found":
        return "RESOURCE_NOT_FOUND"
    else:
        return f"ERROR: {error['message']}"

Best practices for autonomous agents

Start with read-only

Let the agent explore data before writing. Most mistakes come from creating or updating with wrong data.

Confirm before bulk actions

Enrolling 500 contacts in a sequence is hard to undo. Have the agent confirm with the user before bulk writes.

Use source-specific endpoints

Use /sourcing/linkedin/search instead of the generic /sourcing/search. The flat schemas are easier for agents to construct.

Check existing data first

Before creating a contact, search by email or LinkedIn URL to avoid duplicates.

Testing your agent

  1. Create a test API key with read_only scopes only
  2. Run your agent on a read-only task (e.g., “list all active projects and their candidate counts”)
  3. Verify outputs — check that the agent correctly interprets the API responses
  4. Upgrade scopes once read-only works, add write scopes one at a time
  5. Test write operations on a test project before going live