API referencev1

Build on top of your LeadGrid pipelines.

Create dossiers, move them through stages, sync notes and react to events. One REST API, JSON in and out. Available on the Growth plan.

01 — Start here

Overview

The LeadGrid API lets you create and manage dossiers, update stages, attach notes, and sync data with your own tools. Every request returns JSON.

Base URL
https://leadgrid.io/api/v1
Format
JSON (UTF-8)
Auth
Bearer API key
Availability
Growth plan

All timestamps are ISO 8601 in UTC. All amounts are integers or decimals in the smallest practical unit (e.g. euros, not cents). Pagination uses page and per_page query parameters; responses include a meta object with the total count.

02 — Security

Authentication

Every request must carry an API key in the Authorization header. Keys start with lg_live_ and are scoped to one organization.

  1. 1Go to Settings → API and click Create API key.
  2. 2Pick the scopes you need (see table below) and optionally set an expiry. The full key is shown once — copy it immediately.
  3. 3Send the key as a bearer token on every request.
curl
curl https://leadgrid.io/api/v1/dossiers \
  -H "Authorization: Bearer lg_live_your_key_here"
Available scopes
FieldTypeDescription
dossiers:readscopeList and retrieve dossiers.
dossiers:writescopeCreate, update and archive dossiers. Also required to move stages.
flows:readscopeList flows and read their stages.
notes:readscopeRead notes on dossiers.
notes:writescopeAdd new notes to dossiers.
Never expose your key in client-side code. The key has full access to your organization; treat it like a password. If a key leaks, delete it from Settings → API and rotate immediately.
03 — Resources

Dossiers

Dossiers are the entities you track — candidates in recruitment or leads in sales. Each dossier belongs to exactly one flow and sits in exactly one stage.

GET/dossiers

List dossiers in your organization. Supports filtering and pagination.

Query parameters
FieldTypeDescription
typestring'candidate' or 'sales'.
statusstring'active', 'won', 'lost' or 'archived'.
stage_iduuidReturn only dossiers currently in this stage.
pageinteger1-based page number. Default: 1.
per_pageintegerItems per page (max 100). Default: 25.
Example request
curl "https://leadgrid.io/api/v1/dossiers?type=sales&status=active" \
  -H "Authorization: Bearer lg_live_your_key"
Example response
{
  "data": [
    {
      "id": "6f2b…",
      "type": "sales",
      "name": "Rabobank — Talent rollout",
      "company": "Rabobank",
      "contact_person": "Mark de Vries",
      "deal_size": 45000,
      "deal_currency": "EUR",
      "status": "active",
      "current_stage_id": "c3e1…",
      "assigned_to": "ab12…",
      "created_at": "2026-04-10T09:21:14Z"
    }
  ],
  "meta": { "total": 34, "page": 1, "per_page": 25 }
}
POST/dossiers

Create a new dossier. If flow_id is omitted the default flow for the given type is used; the dossier starts in that flow's first stage. Send application/json for a plain create, or multipart/form-data with a 'cv' file field to create the dossier AND attach a PDF CV in one atomic call — if the upload fails, the dossier is rolled back.

Request body
FieldTypeDescription
type*string'candidate' or 'sales'.
name*stringFor candidates: the person's name. For sales: deal or account name.
emailstringPrimary contact email.
phonestringPrimary contact phone.
companystringCandidate: current employer. Sales: target company.
rolestringCandidate: role they're applying for. Sales: role of the contact.
flow_iduuidOverride the default flow. Must belong to your organization.
assigned_touuidUser ID of the member to assign this dossier to.
intake_notesstringDirector/intake notes shown in the dossier drawer.
contact_personstringSales only. Named contact at the target company.
deal_sizenumberSales only. Expected contract value.
deal_currencystringSales only. ISO 4217 currency code (e.g. 'EUR').
cvfile (pdf)multipart/form-data only. Optional PDF CV (max 10 MB). Uploaded and attached in the same call. If the upload fails, the dossier is rolled back.
Example request
# JSON — plain create
curl -X POST https://leadgrid.io/api/v1/dossiers \
  -H "Authorization: Bearer lg_live_your_key" \
  -H "Content-Type: application/json" \
  -d '{
    "type": "sales",
    "name": "KLM — Cabin crew hiring",
    "company": "KLM",
    "contact_person": "Pieter van Leeuwen",
    "deal_size": 62000,
    "deal_currency": "EUR"
  }'

# multipart — create + attach CV in one call
curl -X POST https://leadgrid.io/api/v1/dossiers \
  -H "Authorization: Bearer lg_live_your_key" \
  -F "type=candidate" \
  -F "name=Sophie van Dijk" \
  -F "role=Senior Frontend Engineer" \
  -F "email=sophie@example.com" \
  -F "cv=@./resume.pdf"
Example response
{
  "data": {
    "id": "a91c…",
    "type": "candidate",
    "name": "Sophie van Dijk",
    "cv_url": "<org-id>/dossiers/a91c…/cv.pdf",
    "status": "active",
    "current_stage_id": "b77f…",
    "created_at": "2026-04-15T12:03:40Z"
  }
}
GET/dossiers/:id

Retrieve a single dossier by ID.

Example request
curl https://leadgrid.io/api/v1/dossiers/a91c… \
  -H "Authorization: Bearer lg_live_your_key"
Example response
{
  "data": {
    "id": "a91c…",
    "type": "sales",
    "name": "KLM — Cabin crew hiring",
    "deal_size": 62000,
    "current_stage_id": "b77f…"
  }
}
PATCH/dossiers/:id

Update any subset of fields. Setting current_stage_id moves the dossier to a new stage and emits a dossier.stage_changed webhook.

Request body
FieldTypeDescription
namestringRename the dossier.
emailstringUpdate primary contact email.
phonestringUpdate phone.
companystringUpdate company / employer.
rolestringUpdate role.
contact_personstringSales only.
deal_sizenumberSales only.
deal_currencystringSales only.
statusstring'active', 'won', 'lost' or 'archived'.
assigned_touuidReassign to another member. Null to unassign.
intake_notesstringReplace intake notes.
current_stage_iduuidMove to a new stage. Must belong to the dossier's flow.
Example request
curl -X PATCH https://leadgrid.io/api/v1/dossiers/a91c… \
  -H "Authorization: Bearer lg_live_your_key" \
  -H "Content-Type: application/json" \
  -d '{ "current_stage_id": "d8e2…", "status": "active" }'
Example response
{
  "data": {
    "id": "a91c…",
    "current_stage_id": "d8e2…",
    "status": "active"
  }
}
DELETE/dossiers/:id

Archives the dossier (soft delete). Sets status to 'archived' and emits dossier.deleted. Data is preserved and can be restored via PATCH.

Example request
curl -X DELETE https://leadgrid.io/api/v1/dossiers/a91c… \
  -H "Authorization: Bearer lg_live_your_key"
Example response
{
  "data": {
    "id": "a91c…",
    "status": "archived"
  }
}
POST/dossiers/:id/cv

Upload a PDF CV (max 10 MB) and attach it to an existing dossier. The upload replaces any previous CV. Accepts multipart/form-data with a 'cv' field, or application/pdf with the PDF bytes as the raw body. Emits dossier.updated.

Example request
# multipart/form-data
curl -X POST https://leadgrid.io/api/v1/dossiers/a91c…/cv \
  -H "Authorization: Bearer lg_live_your_key" \
  -F "cv=@./resume.pdf"

# raw application/pdf
curl -X POST https://leadgrid.io/api/v1/dossiers/a91c…/cv \
  -H "Authorization: Bearer lg_live_your_key" \
  -H "Content-Type: application/pdf" \
  --data-binary @./resume.pdf
Example response
{
  "data": {
    "id": "a91c…",
    "cv_url": "<org-id>/dossiers/a91c…/cv.pdf"
  }
}
04 — Resources

Flows

Flows are the pipelines dossiers move through. Each flow has ordered stages with optional deadlines and win probabilities.

GET/flows

List flows in your organization. Stages are nested and ordered by position.

Query parameters
FieldTypeDescription
typestringFilter to 'candidate' or 'sales'.
pageintegerPage number. Default: 1.
per_pageintegerItems per page. Default: 25.
Example request
curl "https://leadgrid.io/api/v1/flows?type=sales" \
  -H "Authorization: Bearer lg_live_your_key"
Example response
{
  "data": [
    {
      "id": "f01a…",
      "name": "Sales Flow",
      "type": "sales",
      "is_default": true,
      "stages": [
        {
          "id": "s1…",
          "name": "Lead",
          "position": 1,
          "deadline_days": 3,
          "win_probability": 14,
          "color": "#FF5C35"
        },
        {
          "id": "s2…",
          "name": "Discovery",
          "position": 2,
          "deadline_days": 5,
          "win_probability": 29,
          "color": "#22C55E"
        }
      ]
    }
  ],
  "meta": { "total": 1, "page": 1, "per_page": 25 }
}
GET/flows/:id/stages

Get the stages for a single flow, sorted by position. Useful if you already know the flow_id and want just the stages.

Example request
curl https://leadgrid.io/api/v1/flows/f01a…/stages \
  -H "Authorization: Bearer lg_live_your_key"
Example response
{
  "data": [
    {
      "id": "s1…",
      "name": "Lead",
      "position": 1,
      "deadline_days": 3,
      "win_probability": 14
    }
  ],
  "meta": { "total": 6, "page": 1, "per_page": 6 }
}
05 — Resources

Notes

Notes are the timeline of updates attached to a dossier. They're sorted oldest-first and are always internal by default.

GET/dossiers/:id/notes

List notes for a dossier, oldest first.

Example request
curl https://leadgrid.io/api/v1/dossiers/a91c…/notes \
  -H "Authorization: Bearer lg_live_your_key"
Example response
{
  "data": [
    {
      "id": "n1…",
      "dossier_id": "a91c…",
      "content": "Had a great first call — strong culture fit.",
      "is_internal": true,
      "created_at": "2026-04-14T09:12:30Z"
    }
  ],
  "meta": { "total": 3, "page": 1, "per_page": 25 }
}
POST/dossiers/:id/notes

Add a new note to a dossier. Emits a note.created webhook.

Request body
FieldTypeDescription
content*stringThe note text. Cannot be empty.
is_internalbooleanDefault: true. Internal notes are not shared.
Example request
curl -X POST https://leadgrid.io/api/v1/dossiers/a91c…/notes \
  -H "Authorization: Bearer lg_live_your_key" \
  -H "Content-Type: application/json" \
  -d '{ "content": "Followed up by email." }'
Example response
{
  "data": {
    "id": "n2…",
    "dossier_id": "a91c…",
    "content": "Followed up by email.",
    "is_internal": true,
    "created_at": "2026-04-15T12:04:10Z"
  }
}
06 — Events

Webhooks

Configure a webhook endpoint in Settings → API. LeadGrid sends a POST request with a JSON body when any of these events happen.

Event types
FieldTypeDescription
dossier.createdeventA new dossier was created (via API, UI or email).
dossier.updatedeventAny field on a dossier changed. Fires alongside stage_changed when applicable.
dossier.stage_changedeventcurrent_stage_id was updated.
dossier.deletedeventDossier was archived (status set to 'archived').
note.createdeventA new note was added to a dossier.
Example payload
POST https://your-app.com/webhooks/leadgrid
Content-Type: application/json
X-LeadGrid-Signature: t=1713178230,v1=3b2c4f…

{
  "id": "evt_…",
  "type": "dossier.stage_changed",
  "created_at": "2026-04-15T12:04:10Z",
  "data": {
    "id": "a91c…",
    "current_stage_id": "d8e2…",
    "status": "active"
  }
}

Signature verification

Each webhook request includes an X-LeadGrid-Signature header containing a timestamp and an HMAC-SHA256 signature of `${timestamp}.${body}` signed with your endpoint’s secret. Verify it before trusting the payload.

node.js (verification)
import crypto from "node:crypto";

export function verifyLeadGridSignature(
  header: string,
  body: string,
  secret: string,
) {
  const parts = Object.fromEntries(
    header.split(",").map((p) => p.split("=")),
  );
  const { t, v1 } = parts as { t: string; v1: string };

  const expected = crypto
    .createHmac("sha256", secret)
    .update(`${t}.${body}`)
    .digest("hex");

  return crypto.timingSafeEqual(
    Buffer.from(expected),
    Buffer.from(v1),
  );
}
07 — Reference

Errors & rate limits

Errors use standard HTTP status codes. The body always contains an error object with a stable code and a human-readable message.

Error shape
{
  "error": {
    "code": "not_found",
    "message": "Dossier not found."
  }
}
Common error codes
FieldTypeDescription
unauthorized401Missing, malformed or invalid API key. Also returned for expired keys.
plan_required402Your organization is on Free or Pro. API access requires Growth.
forbidden403The API key doesn't include the required scope for this action.
not_found404The resource doesn't exist, or doesn't belong to your organization.
invalid_body400Missing required field, unknown value or malformed JSON.
rate_limited429You've exceeded the rate limit for your plan. Retry after the time in the Retry-After header.
internal500Unexpected server error. Safe to retry.
Growth rate limit
600 req / min / key
429 response
Retry-After header in seconds
Every response includes X-RateLimit-Limit and X-RateLimit-Remaining headers so you can back off before hitting the cap.