CRM

Sales cycles (CRM board)

The board leads move on: sales cycles and their stages, lead stats and funnel, follow-ups due, moving leads in bulk and turning them into deals.

Each workspace has one or more sales cycles (e.g. “New customers”, “Distributors”). A cycle is a list of stages; every contact sits in one cycle (sales_cycle_id) at one stage (pipeline_stage). “New leads” and “Lost” always exist. Each cycle has a deal pipeline with the same stages, kept in step: moving a deal moves its contact, and the other way round.

In a chat
Copy a ready promptPaste it in any AI: it reads the docs and follows the rules of the API.
Open in Claude or ChatGPTA new conversation with the prompt already written.
This page as MarkdownFor an AI that cannot open links: paste the text itself.
In your editor
Claude Code or CodexAdd this line to CLAUDE.md or AGENTS.md.Meetzy API reference (read before calling the API): https://www.meetzy.ai/llms-full.txt
CursorSettings › Indexing & Docs › Add Doc, then type @Meetzy in the chat.https://www.meetzy.ai/llms-full.txt
Connect Meetzy as tools (MCP)The AI can then read and change your CRM itself — Claude Code:claude mcp add --transport http meetzy https://meetzy.me/api/mcp/mcp
Same for Codexcodex mcp add meetzy --url https://meetzy.me/api/mcp/mcp
GET/crm/cycles

List sales cycles

Every cycle with its stages (fixed for New leads / Lost, won for the won stage) and how many leads it holds.

Scope: read
Request
curl "https://meetzy.me/api/v1/crm/cycles" \
  -H "Authorization: Bearer $MEETZY_API_KEY"
const res = await fetch("https://meetzy.me/api/v1/crm/cycles", {
  headers: { Authorization: `Bearer ${process.env.MEETZY_API_KEY}` },
});
const data = await res.json();
import os, requests

res = requests.get(
    "https://meetzy.me/api/v1/crm/cycles",
    headers={"Authorization": f"Bearer {os.environ['MEETZY_API_KEY']}"},
)
data = res.json()
Response200
{
  "configured": false,
  "multi_supported": true,
  "cycles": [
    {
      "id": "default",
      "name": "Main pipeline",
      "color": "#007AFF",
      "is_default": true,
      "stages": [
        {
          "id": "new_leads",
          "name": "New leads",
          "color": "#8E8E93",
          "fixed": true
        },
        {
          "id": "in_discussion",
          "name": "In discussion",
          "description": null,
          "color": "#5856D6",
          "won": false
        },
        {
          "id": "negotiation",
          "name": "Negotiation",
          "description": null,
          "color": "#007AFF",
          "won": false
        }
      ],
      "count": 1
    }
  ]
}
PUT/crm/cycles

Save the sales cycles

Replaces every cycle at once (the first one is the default). Stages are listed in order and the last one is the won stage. A renamed stage keeps its leads when you send its previous_id; leads of a removed stage go back to New leads, leads of a removed cycle go to the default one. Don’t include New leads and Lost — they are added.

Scope: write

Body

cyclesobject[]required
Each { id?, name, color, stages: [{ name, description?, previous_id? }] }. Omit id for a new cycle. A stage description is its playbook (up to 1500 characters): shown on every lead at that stage, and followed by Claude.

Returns the saved cycles (same shape as GET /crm/cycles) plus moved (leads) and deals_moved.

Request
curl -X PUT "https://meetzy.me/api/v1/crm/cycles" \
  -H "Authorization: Bearer $MEETZY_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "cycles": [
      {
        "id": "default",
        "name": "New customers",
        "color": "#007AFF",
        "stages": [
          {
            "name": "In discussion",
            "previous_id": "in_discussion"
          },
          {
            "name": "Proposal sent"
          },
          {
            "name": "Won"
          }
        ]
      },
      {
        "name": "Distributors",
        "color": "#AF52DE",
        "stages": [
          {
            "name": "Contacted"
          },
          {
            "name": "Contract signed"
          }
        ]
      }
    ]
  }'
const res = await fetch("https://meetzy.me/api/v1/crm/cycles", {
  method: "PUT",
  headers: { Authorization: `Bearer ${process.env.MEETZY_API_KEY}`, "Content-Type": "application/json" },
  body: JSON.stringify({
    "cycles": [
      {
        "id": "default",
        "name": "New customers",
        "color": "#007AFF",
        "stages": [
          {
            "name": "In discussion",
            "previous_id": "in_discussion"
          },
          {
            "name": "Proposal sent"
          },
          {
            "name": "Won"
          }
        ]
      },
      {
        "name": "Distributors",
        "color": "#AF52DE",
        "stages": [
          {
            "name": "Contacted"
          },
          {
            "name": "Contract signed"
          }
        ]
      }
    ]
  }),
});
const data = await res.json();
import os, requests

res = requests.put(
    "https://meetzy.me/api/v1/crm/cycles",
    headers={"Authorization": f"Bearer {os.environ['MEETZY_API_KEY']}"},
    json={
        "cycles": [
            {
                "id": "default",
                "name": "New customers",
                "color": "#007AFF",
                "stages": [
                    {
                        "name": "In discussion",
                        "previous_id": "in_discussion"
                    },
                    {
                        "name": "Proposal sent"
                    },
                    {
                        "name": "Won"
                    }
                ]
            },
            {
                "name": "Distributors",
                "color": "#AF52DE",
                "stages": [
                    {
                        "name": "Contacted"
                    },
                    {
                        "name": "Contract signed"
                    }
                ]
            }
        ]
    },
)
data = res.json()
Response200

See the notes on the left.

GET/crm/stages

Stages of the default cycle

Ids, names and colours of the default cycle’s stages — the values pipeline_stage takes.

Scope: read
Request
curl "https://meetzy.me/api/v1/crm/stages" \
  -H "Authorization: Bearer $MEETZY_API_KEY"
const res = await fetch("https://meetzy.me/api/v1/crm/stages", {
  headers: { Authorization: `Bearer ${process.env.MEETZY_API_KEY}` },
});
const data = await res.json();
import os, requests

res = requests.get(
    "https://meetzy.me/api/v1/crm/stages",
    headers={"Authorization": f"Bearer {os.environ['MEETZY_API_KEY']}"},
)
data = res.json()
Response200
{
  "stages": [
    {
      "id": "new_leads",
      "name": "New leads",
      "color": "#6B7280"
    },
    {
      "id": "in_discussion",
      "name": "In discussion",
      "color": "#3B82F6"
    }
  ]
}
GET/crm/lead-stats

Lead statistics and funnel

For one cycle: leads per stage (and how many are hot), totals (active, hot, warm, won, lost, new this period and change, follow-ups due, stale, unassigned, conversion rate), the funnel, leads by source and by owner.

Scope: read

Query parameters

periodstring
week, month (default), quarter, year…
ownerstring
me or a teammate’s email.
cyclestring
Cycle id. Default: the default cycle.
Request
curl "https://meetzy.me/api/v1/crm/lead-stats?period=quarter&owner=me" \
  -H "Authorization: Bearer $MEETZY_API_KEY"
const res = await fetch("https://meetzy.me/api/v1/crm/lead-stats?period=quarter&owner=me", {
  headers: { Authorization: `Bearer ${process.env.MEETZY_API_KEY}` },
});
const data = await res.json();
import os, requests

res = requests.get(
    "https://meetzy.me/api/v1/crm/lead-stats?period=quarter&owner=me",
    headers={"Authorization": f"Bearer {os.environ['MEETZY_API_KEY']}"},
)
data = res.json()
Response200
{
  "cycle": "default",
  "cycles": [
    {
      "id": "default",
      "name": "Main pipeline",
      "color": "#007AFF",
      "count": 1,
      "active": 1,
      "won": 0,
      "hot": 0
    }
  ],
  "stages": [
    {
      "id": "new_leads",
      "name": "New leads",
      "color": "#6B7280",
      "count": 1,
      "hot": 0
    },
    {
      "id": "in_discussion",
      "name": "In discussion",
      "color": "#3B82F6",
      "count": 0,
      "hot": 0
    }
  ],
  "won_stage": "won",
  "first_stage": "new_leads",
  "totals": {
    "total": 1,
    "active": 1,
    "hot": 0,
    "warm": 0,
    "lost": 0,
    "won": 0,
    "new": 1,
    "new_change": 100,
    "won_period": 0,
    "lost_period": 0,
    "followups_due": 0,
    "stale": 0,
    "with_email": 1,
    "unassigned": 1,
    "conversion_rate": 0
  },
  "funnel": [
    {
      "id": "new_leads",
      "name": "New leads",
      "color": "#6B7280",
      "reached": 1
    },
    {
      "id": "in_discussion",
      "name": "In discussion",
      "color": "#3B82F6",
      "reached": 0
    }
  ],
  "by_source": [
    {
      "source": "manual",
      "count": 1
    }
  ],
  "by_owner": [
    {
      "owner": "unassigned",
      "count": 1,
      "hot": 0,
      "won": 0
    }
  ]
}
GET/crm/followups

Follow-ups due

Contacts whose follow-up date (reminder_date) is today, late, or within the next days.

Scope: read

Query parameters

daysinteger
Look ahead this many days. Default 7.
ownerstring
me for your own.
Request
curl "https://meetzy.me/api/v1/crm/followups" \
  -H "Authorization: Bearer $MEETZY_API_KEY"
const res = await fetch("https://meetzy.me/api/v1/crm/followups", {
  headers: { Authorization: `Bearer ${process.env.MEETZY_API_KEY}` },
});
const data = await res.json();
import os, requests

res = requests.get(
    "https://meetzy.me/api/v1/crm/followups",
    headers={"Authorization": f"Bearer {os.environ['MEETZY_API_KEY']}"},
)
data = res.json()
Response200
{
  "followups": [
    {
      "id": "e43414d6-f56f-42be-a64c-a836d6015efc",
      "display_name": "Camille Laurent",
      "company": "Payfit",
      "reminder_date": "2026-10-08",
      "reminder_note": "Send the pricing",
      "assigned_to": "olivia@acme.com",
      "pipeline_stage": "in_discussion",
      "lead_score": 55
    }
  ]
}
POST/contacts/bulk/move

Move leads to a cycle and stage

Moves up to 5,000 contacts to a stage of a sales cycle; their open deals follow to that cycle’s pipeline.

Scope: write

Body

contact_idsuuid[]required
Contacts to move.
cycle_idstring
Target cycle. Default: the default cycle.
stage_idstring
Target stage. Default: the first stage.
Request
curl -X POST "https://meetzy.me/api/v1/contacts/bulk/move" \
  -H "Authorization: Bearer $MEETZY_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "contact_ids": [
      "e43414d6-f56f-42be-a64c-a836d6015efc"
    ],
    "cycle_id": "default",
    "stage_id": "in_discussion"
  }'
const res = await fetch("https://meetzy.me/api/v1/contacts/bulk/move", {
  method: "POST",
  headers: { Authorization: `Bearer ${process.env.MEETZY_API_KEY}`, "Content-Type": "application/json" },
  body: JSON.stringify({
    "contact_ids": [
      "e43414d6-f56f-42be-a64c-a836d6015efc"
    ],
    "cycle_id": "default",
    "stage_id": "in_discussion"
  }),
});
const data = await res.json();
import os, requests

res = requests.post(
    "https://meetzy.me/api/v1/contacts/bulk/move",
    headers={"Authorization": f"Bearer {os.environ['MEETZY_API_KEY']}"},
    json={
        "contact_ids": ["e43414d6-f56f-42be-a64c-a836d6015efc"],
        "cycle_id": "default",
        "stage_id": "in_discussion"
    },
)
data = res.json()
Response201
{
  "moved": 1,
  "cycle_id": "default",
  "stage_id": "in_discussion",
  "stage_name": "In discussion"
}
POST/crm/convert-to-deals

Turn leads into deals

Creates a deal for each lead (up to 2,000) — chosen by id or by stage — in their cycle’s pipeline at the same stage. Leads that already have an open deal are skipped.

Scope: write

Body

lead_idsuuid[]
Leads to convert.
stage_idsstring[]
Or every lead in these stages.
amountnumber
Amount of each new deal. Default 0.
Request
curl -X POST "https://meetzy.me/api/v1/crm/convert-to-deals" \
  -H "Authorization: Bearer $MEETZY_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "stage_ids": [
      "negotiation"
    ],
    "amount": 5000
  }'
const res = await fetch("https://meetzy.me/api/v1/crm/convert-to-deals", {
  method: "POST",
  headers: { Authorization: `Bearer ${process.env.MEETZY_API_KEY}`, "Content-Type": "application/json" },
  body: JSON.stringify({
    "stage_ids": [
      "negotiation"
    ],
    "amount": 5000
  }),
});
const data = await res.json();
import os, requests

res = requests.post(
    "https://meetzy.me/api/v1/crm/convert-to-deals",
    headers={"Authorization": f"Bearer {os.environ['MEETZY_API_KEY']}"},
    json={
        "stage_ids": ["negotiation"],
        "amount": 5000
    },
)
data = res.json()
Response201
{
  "created": 12,
  "skipped": 3
}
GET/crm/settings

CRM housekeeping settings

inactivity_days_lost: after how many days without activity a lead is moved to Lost automatically (0 = never).

Scope: read
Request
curl "https://meetzy.me/api/v1/crm/settings" \
  -H "Authorization: Bearer $MEETZY_API_KEY"
const res = await fetch("https://meetzy.me/api/v1/crm/settings", {
  headers: { Authorization: `Bearer ${process.env.MEETZY_API_KEY}` },
});
const data = await res.json();
import os, requests

res = requests.get(
    "https://meetzy.me/api/v1/crm/settings",
    headers={"Authorization": f"Bearer {os.environ['MEETZY_API_KEY']}"},
)
data = res.json()
Response200
{
  "inactivity_days_lost": 0
}
PATCH/crm/settings

Change CRM housekeeping settings

Scope: write

Body

inactivity_days_lostintegerrequired
Days without activity before a lead is lost (0 = never, max 3650).
Request
curl -X PATCH "https://meetzy.me/api/v1/crm/settings" \
  -H "Authorization: Bearer $MEETZY_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "inactivity_days_lost": 90
  }'
const res = await fetch("https://meetzy.me/api/v1/crm/settings", {
  method: "PATCH",
  headers: { Authorization: `Bearer ${process.env.MEETZY_API_KEY}`, "Content-Type": "application/json" },
  body: JSON.stringify({
    "inactivity_days_lost": 90
  }),
});
const data = await res.json();
import os, requests

res = requests.patch(
    "https://meetzy.me/api/v1/crm/settings",
    headers={"Authorization": f"Bearer {os.environ['MEETZY_API_KEY']}"},
    json={
        "inactivity_days_lost": 90
    },
)
data = res.json()
Response200
{
  "inactivity_days_lost": 90
}