API & MCP Documentation

Automate Tootela with code. Connect Claude and AI agents via the MCP server, or call the JSON-RPC 2.0 API directly from any language.

BASE URLhttps://api.tootela.net/api/mcp

Overview

Tootela exposes the full platform via a single JSON-RPC 2.0 endpoint that is fully compatible with the Model Context Protocol (MCP). One endpoint, 40+ tools, 9 modules. This means you can:

  • Call any tool directly from Python, JavaScript, curl, or any HTTP client
  • Connect any MCP-compatible AI agent (Claude, GPT, Gemini, and more) to your whole Tootela workspace
  • Automate across CRM (contacts, deals, pipelines), Projects (tasks, sprints, epics), Helpdesk (tickets, replies), HR (employees, leave), Expenses, Onboarding, Recruitment, and Forms
  • Build outreach scripts that discover leads, draft emails, and sync deals to the right pipeline

Authentication

All requests require a Bearer token. Generate one in your workspace at Settings → API Tokens.

Authorization: Bearer tla_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

Tokens are scoped to your organisation. Use environment variables — never hardcode them.

MCP Server

Add Tootela to any MCP-compatible AI client (Claude Desktop, Cursor, Windsurf, and more) and let it create contacts, log activities, and manage deals directly from your workflow.

MCP client config

Most MCP clients accept a config like this (example: Claude Desktop at ~/Library/Application Support/Claude/claude_desktop_config.json):

{
  "mcpServers": {
    "tootela": {
      "url": "https://api.tootela.net/api/mcp",
      "transport": "http",
      "headers": {
        "Authorization": "Bearer tla_YOUR_TOKEN_HERE"
      }
    }
  }
}

Request envelope

Every call uses the same JSON-RPC 2.0 structure:

{
  "jsonrpc": "2.0",
  "id": "1",
  "method": "tools/call",
  "params": {
    "name": "create_company",
    "arguments": {
      "name": "Acme Corp",
      "domain": "acme.com",
      "industry": "SaaS"
    }
  }
}

CRM & Email

Manage contacts, companies, deals, pipelines, activities, and send personalised emails.

name* = required.

list_contacts

List CRM contacts. Filter by name or email with an optional search string.

ParameterTypeDescription
searchstringPartial match on name or email
limitnumberMax results (default 50, max 200)
create_contact

Create a new CRM contact, optionally linked to an existing company.

ParameterTypeDescription
emailstringEmail address
first_namestringFirst name
last_namestringLast name
phonestringPhone number
job_titlestringRole, e.g. CEO, Head of Growth
company_idstringUUID of an existing company
sourcestringe.g. outbound, linkedin, referral
notesstringFree-form notes
list_companies

List CRM companies. Returns id, name, domain, website, industry, size, city, country, custom_fields (JSONB) and created_at.

ParameterTypeDescription
searchstringPartial match on company name
limitnumberMax results (default 50, max 200)
create_company

Create a new CRM company record.

ParameterTypeDescription
name*stringCompany name
domainstringWebsite domain, e.g. acme.com
websitestringFull URL — auto-derived from domain if omitted
industrystringe.g. Fintech, SaaS, Marketing Agency
sizestringe.g. 1-10, 11-50, 51-200
citystringCity
countrystringCountry
create_deal

Create a deal in a sales pipeline. Automatically placed at the first stage of the target pipeline.

ParameterTypeDescription
name*stringDeal name, e.g. "Outreach — Acme Corp"
valuenumberMonetary value
currencystringISO code (default: EUR)
company_idstringUUID of linked company
contact_idstringUUID of linked contact
pipeline_idstringUUID of the target pipeline — uses default pipeline if omitted
expected_close_datestringISO date YYYY-MM-DD
list_pipelines

List all sales pipelines in the organisation. Use this to get a pipeline_id before creating deals.

create_pipeline

Create a sales pipeline with default stages: Lead → Qualified → Proposal → Negotiation → Won. Useful for organising deals by geography, campaign, or quarter.

ParameterTypeDescription
name*stringPipeline name, e.g. "France", "Germany", "Outbound Q2"
update_contact

Update fields on an existing contact. Pass only the fields you want to change.

ParameterTypeDescription
id*stringUUID of the contact to update
emailstringNew email address
first_namestringFirst name
last_namestringLast name
phonestringPhone number
job_titlestringRole
company_idstringUUID of linked company
lead_statusstringnew | contacted | qualified | converted | lost
notesstringFree-form notes
update_company

Update fields on an existing company. Pass only the fields you want to change.

ParameterTypeDescription
id*stringUUID of the company to update
namestringCompany name
domainstringDomain, e.g. acme.com
websitestringFull website URL
industrystringIndustry sector
sizestringe.g. 1-10, 11-50, 51-200
citystringCity
countrystringCountry
delete_company

Delete a company by id. Scoped to the token's organisation. Deals attached to the company keep their company_id but the reference will resolve to null.

ParameterTypeDescription
id*stringUUID of the company to delete
list_deals

List CRM deals with optional filters. Use to check existing deals before creating duplicates.

ParameterTypeDescription
pipeline_idstringFilter by pipeline UUID
contact_idstringFilter by contact UUID
company_idstringFilter by company UUID
statusstringopen | won | lost
searchstringPartial match on deal name
limitnumberMax results (default 50, max 200)
update_deal

Update fields on an existing deal — move it to a new stage, change value, mark won/lost.

ParameterTypeDescription
id*stringUUID of the deal to update
namestringDeal name
valuenumberMonetary value
statusstringopen | won | lost
stage_idstringUUID of the new pipeline stage
pipeline_idstringMove to a different pipeline
expected_close_datestringISO date YYYY-MM-DD
list_pipeline_stages

List stages of a specific pipeline. Use stage UUIDs with update_deal to move deals forward.

ParameterTypeDescription
pipeline_id*stringUUID of the pipeline
list_activities

List CRM activities. Filter by contact, deal, or type.

ParameterTypeDescription
contact_idstringFilter by contact UUID
deal_idstringFilter by deal UUID
typestringcall | email | meeting | note | task
limitnumberMax results (default 50, max 200)
log_activity

Log a CRM activity (email, call, meeting, note, task) against a contact or deal.

ParameterTypeDescription
type*stringcall | email | meeting | note | task
title*stringActivity title or email subject
notesstringBody / description / email content
contact_idstringUUID of linked contact
deal_idstringUUID of linked deal
due_datestringISO datetime for tasks / reminders
update_activity

Update an existing activity: change notes, due date, or mark it as done.

ParameterTypeDescription
id*stringUUID of the activity to update
titlestringNew title
notesstringUpdated notes / body
due_datestringISO datetime
donebooleanMark as completed
create_contact_point

Log a direct interaction with a contact (call, email, LinkedIn, WhatsApp, etc.) including the outcome. Useful for tracking outreach history.

ParameterTypeDescription
type*stringcall | email | meeting | linkedin | whatsapp | sms | note | other
contact_idstringUUID of the contact
company_idstringUUID of the company (if no specific contact)
summarystringWhat was discussed / message content
outcomestringpositive | neutral | negative | no_response
contacted_atstringISO datetime (defaults to now)
send_email

Send a personalised email via Tootela. Use for cold outreach, follow-ups, and automated sequences.

ParameterTypeDescription
to*stringRecipient email address
subject*stringSubject line
body*stringPlain text or HTML
to_namestringRecipient display name
from_namestringSender display name
reply_tostringReply-to address

Custom Fields

Custom fields let you extend companies, contacts, and deals with organisation-specific attributes (e.g. Contact Page URL, Lead Source, Outreach Status).

Data model: custom field values are stored on the entity itself, inside a custom_fields JSONB column keyed by field_key. Use the set_*_custom_field tools below : they resolve the field_key from the definition and merge into the JSONB so the UI picks the value up immediately.

name* = required.

list_custom_field_defs

List custom field definitions (name, key, type, options, order). Use this first to discover field_def_id values.

ParameterTypeDescription
entity_typestringcontact | company | deal
pipeline_idstringFilter to deal-level fields scoped to a specific pipeline
create_custom_field_def

Create a new custom field on a CRM entity. field_key is auto-generated from name if omitted (snake_cased). Use options for select fields.

ParameterTypeDescription
entity_type*stringcontact | company | deal
name*stringDisplay name, e.g. "Contact Page URL"
field_keystringStable machine key. Auto-generated from name if omitted.
field_typestringtext | number | date | boolean | select (default: text)
optionsarrayArray of string options (only for field_type="select")
requiredbooleanMark the field as required (default false)
ordernumberDisplay order, ascending (default 0)
pipeline_idstringUUID of a pipeline to scope the field to (deals only)
delete_custom_field_def

Delete a custom field definition. Existing values in entity JSONB blobs are left in place but become orphaned.

ParameterTypeDescription
id*stringUUID of the custom field definition
list_custom_field_values

Diagnostic: list rows from the legacy crm_custom_field_values key/value table. The UI does not read from this table; use it to audit historical data or migrate into the entity JSONB columns.

ParameterTypeDescription
entity_idstringSingle entity (company/contact/deal) UUID to filter by
entity_idsarrayMultiple entity UUIDs to filter by
field_def_idstringFilter by a single custom field definition UUID
field_def_idsarrayFilter by multiple custom field definition UUIDs
limitnumberMax results (default 500, max 5000)
set_company_custom_field

Set a single custom field value on a company. Resolves field_key from field_def_id and merges into crm_companies.custom_fields JSONB (what the UI reads).

ParameterTypeDescription
company_id*stringUUID of the company
field_def_id*stringUUID of the custom field definition
value*stringValue to set (stringified; booleans as "true"/"false")
set_contact_custom_field

Set a single custom field value on a contact. Merges into crm_contacts.custom_fields JSONB.

ParameterTypeDescription
contact_id*stringUUID of the contact
field_def_id*stringUUID of the custom field definition
value*stringValue to set
set_deal_custom_field

Set a single custom field value on a deal. Merges into crm_deals.custom_fields JSONB.

ParameterTypeDescription
deal_id*stringUUID of the deal
field_def_id*stringUUID of the custom field definition
value*stringValue to set

Projects

Manage projects, tasks, sprints, and epics. All operations are scoped to the token's organisation.

list_projects

List all projects in the organisation.

create_project

Create a new project.

ParameterTypeDescription
name*stringProject name
descriptionstringOptional description
colorstringHex color (e.g. #6366f1)
iconstringEmoji or icon key
list_tasks

List project tasks with filters.

ParameterTypeDescription
project_idstringFilter by project UUID
sprint_idstringFilter by sprint UUID
assignee_idstringFilter by assignee user UUID
statusstringtodo | in_progress | in_review | done
prioritystringlow | medium | high | urgent
searchstringPartial match on title
limitnumberMax results (default 50, max 200)
create_task

Create a new task in a project.

ParameterTypeDescription
project_id*stringUUID of the project
title*stringTask title
descriptionstringTask description
statusstringtodo | in_progress | in_review | done
prioritystringlow | medium | high | urgent
assignee_idstringUUID of assignee
sprint_idstringUUID of sprint
stage_idstringUUID of board stage
due_datestringISO date YYYY-MM-DD
story_pointsnumberEstimation in points
labelsarrayList of label strings
item_typestringtask | feature | user_story | epic | subtask
update_task

Update a task. Pass only the fields you want to change.

ParameterTypeDescription
id*stringUUID of the task
titlestringNew title
descriptionstringNew description
statusstringtodo | in_progress | in_review | done
prioritystringlow | medium | high | urgent
assignee_idstringUUID of user, or null to unassign
sprint_idstringUUID of sprint, or null for backlog
due_datestringISO date YYYY-MM-DD
list_sprints

List sprints in the organisation.

ParameterTypeDescription
project_idstringFilter by project UUID
statusstringplanned | active | completed
create_sprint

Create a new sprint (auto-numbered within the project).

ParameterTypeDescription
project_id*stringUUID of the project
name*stringSprint name
goalstringSprint goal description
start_datestringISO date YYYY-MM-DD
end_datestringISO date YYYY-MM-DD
list_epics

List epics / user stories.

ParameterTypeDescription
project_idstringFilter by project UUID
statusstringdraft | ready | in_progress | completed
create_epic

Create a new epic / user story.

ParameterTypeDescription
project_id*stringUUID of the project
title*stringEpic title
descriptionstringDescription
acceptance_criteriastringAcceptance criteria
prioritystringlow | medium | high | urgent
statusstringdraft | ready | in_progress | completed
story_pointsnumberEstimated points
list_members

List organisation members. Useful to resolve names to user UUIDs for task assignment.

Helpdesk

Manage support tickets, replies, and statuses.

list_tickets

List helpdesk tickets.

ParameterTypeDescription
statusstringopen | in_progress | waiting | resolved | closed
prioritystringlow | normal | high | urgent
assigned_tostringFilter by assigned agent UUID
searchstringPartial match on subject
limitnumberMax results
create_ticket

Create a new helpdesk ticket on behalf of a customer.

ParameterTypeDescription
subject*stringTicket subject
prioritystringlow | normal | high | urgent
customer_namestringCustomer display name
customer_emailstringCustomer email
channelstringwidget | page | email | api
initial_messagestringFirst message from the customer
update_ticket

Update ticket status, priority, or assignee.

ParameterTypeDescription
id*stringUUID of the ticket
statusstringopen | in_progress | waiting | resolved | closed
prioritystringlow | normal | high | urgent
assigned_tostringUUID of agent
reply_ticket

Add an agent reply to a ticket. Marks first_response_at automatically.

ParameterTypeDescription
ticket_id*stringUUID of the ticket
content*stringReply message body
is_internalbooleanTrue for internal notes

HR

Access employees, departments, and leave requests.

list_employees

List HR employees in the organisation.

ParameterTypeDescription
statusstringactive | inactive | on_leave | terminated
department_idstringFilter by department UUID
searchstringPartial match on name or email
list_departments

List organisation departments.

list_leave_requests

List time-off / leave requests.

ParameterTypeDescription
statusstringpending | approved | rejected | cancelled
employee_idstringFilter by employee UUID
limitnumberMax results
update_leave_request

Approve, reject, or cancel a time-off request.

ParameterTypeDescription
id*stringUUID of the leave request
status*stringapproved | rejected | cancelled
review_commentstringOptional comment

Expenses

List, approve, or reject expense reports. Approvals respect the multi-step approval chain.

list_expenses

List expense reports.

ParameterTypeDescription
statusstringdraft | submitted | approved | rejected
user_idstringFilter by submitter UUID
limitnumberMax results
approve_expense

Approve an expense. Moves to the next approver in the chain, or marks as final approved.

ParameterTypeDescription
id*stringUUID of the expense
notesstringOptional approval comment
reject_expense

Reject an expense.

ParameterTypeDescription
id*stringUUID of the expense
reasonstringRejection reason

Onboarding

Track new hire onboarding journeys and task completion.

list_onboarding_processes

List onboarding journeys.

ParameterTypeDescription
statusstringnot_started | in_progress | completed | cancelled
employee_idstringFilter by employee UUID
list_onboarding_tasks

List onboarding task instances.

ParameterTypeDescription
process_idstringFilter by process UUID
assigned_tostringFilter by assignee user UUID
statusstringpending | in_progress | completed | skipped

Recruitment

Manage job postings and candidate pipelines.

list_jobs

List job postings.

ParameterTypeDescription
statusstringdraft | published | closed | archived
searchstringPartial match on title
create_job

Create a new job posting.

ParameterTypeDescription
title*stringJob title
department_idstringUUID of department
employment_typestringfull_time | part_time | contract | internship
locationstringJob location
descriptionstringJob description
salary_minnumberMinimum salary
salary_maxnumberMaximum salary
statusstringdraft | published
list_candidates

List job candidates.

ParameterTypeDescription
job_posting_idstringFilter by job UUID
stagestringapplied | screening | interview | offer | hired | rejected
searchstringPartial match on name or email

Forms

Read published forms and their submissions.

list_forms

List forms in the organisation.

ParameterTypeDescription
statusstringdraft | published
list_form_submissions

List submissions for a given form.

ParameterTypeDescription
form_id*stringUUID of the form
limitnumberMax results (default 50, max 200)

Code Examples

Python: country-named pipeline + full outreach flow

import httpx, json, os

MCP_URL = "https://api.tootela.net/api/mcp"
TOKEN   = os.environ["TOOTELA_TOKEN"]

def call(tool, args):
    r = httpx.post(
        MCP_URL,
        headers={"Authorization": f"Bearer {TOKEN}", "Content-Type": "application/json"},
        json={"jsonrpc": "2.0", "id": "1", "method": "tools/call",
              "params": {"name": tool, "arguments": args}},
        timeout=10,
    )
    result = r.json().get("result", {})
    content = result.get("content", [])
    return json.loads(content[0]["text"]) if content else result

# 1. Find or create a "France" pipeline
pipelines = call("list_pipelines", {})["pipelines"]
france = next((p for p in pipelines if p["name"] == "France"), None)
if not france:
    france = call("create_pipeline", {"name": "France"})["pipeline"]

# 2. Create company
company = call("create_company", {
    "name": "Acme Agency",
    "domain": "acme-agency.fr",
    "industry": "Marketing Agency",
    "city": "Bordeaux",
    "country": "France",
})["company"]

# 3. Create deal in the France pipeline
deal = call("create_deal", {
    "name": f"Outreach — {company['name']}",
    "company_id": company["id"],
    "pipeline_id": france["id"],
})["deal"]

# 4. Log the email draft as an activity
call("log_activity", {
    "type": "email",
    "title": "Cold outreach",
    "notes": "Hi team, we'd love to show you Tootela...",
    "deal_id": deal["id"],
})

curl: list pipelines

curl -X POST https://api.tootela.net/api/mcp \
  -H "Authorization: Bearer tla_YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0", "id": "1",
    "method": "tools/call",
    "params": { "name": "list_pipelines", "arguments": {} }
  }'

curl: send an email

curl -X POST https://api.tootela.net/api/mcp \
  -H "Authorization: Bearer tla_YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0", "id": "1",
    "method": "tools/call",
    "params": {
      "name": "send_email",
      "arguments": {
        "to": "founder@startup.io",
        "to_name": "Alex",
        "subject": "Quick question about your stack",
        "body": "Hi Alex,\n\nI noticed you...",
        "reply_to": "eliot@tootela.net"
      }
    }
  }'

Error Handling

Tool errors return HTTP 200 with a JSON-RPC error object. Auth failures return HTTP 401.

// Auth failure — HTTP 401
{"jsonrpc":"2.0","id":null,"error":{"code":-32001,"message":"Unauthorized: invalid or missing Bearer token"}}

// Tool error — HTTP 200
{"jsonrpc":"2.0","id":"1","error":{"code":-32603,"message":"duplicate key value violates unique constraint"}}

// Unknown method — HTTP 200
{"jsonrpc":"2.0","id":"1","error":{"code":-32601,"message":"Method not found: tools/unknown"}}

Ready to automate?

Questions? support@tootela.net

Get API token →