DocsConcepts

Calling tools

Every tool is one call over REST or MCP. Every reply comes in the same envelope, with what the call cost and what is left.

On this page
  1. Finding a tool
  2. Over REST
  3. The reply
  4. Over MCP
  5. What a call costs
  6. Retrying safely
  7. Background calls
  8. When a call is refused
  9. Limits on a request
  10. JSON-RPC errors

Finding a tool

  • Each tool's page in the tools directory shows its inputs, an example call, a real reply and its errors. Read it before calling a tool for the first time. Its Markdown is at the page's address plus .md.
  • GET https://api.conecto.chat/v1/tools lists every tool, and ?area=crm one area. GET https://api.conecto.chat/v1/tools/<name> gives one tool's input schema. Neither needs a key.
  • Over MCP, tools/list on an area's server lists its tools with their input schemas and prices.

Over REST

POST https://api.conecto.chat/v1/tools/<name> with the arguments as a JSON object, and the key in the Authorization header. The examples on this page are the tools' documented calls, made for the docs' two example businesses: Halverson Cycle & Repair, a bike shop in Portland, and Hansen & Voss, a restaurant-equipment wholesaler in Hamburg.

curl

curl -X POST https://api.conecto.chat/v1/tools/crm_save_contact \
  -H "Authorization: Bearer $CONECTO_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "email": "jana@weber-kuechenplanung.example",
    "name": "Jana Weber",
    "title": "Kitchen planner",
    "company": "Weber Küchenplanung",
    "phone": "+49 40 66969 120",
    "location": "Hamburg",
    "lifecycle_stage": "lead",
    "tags": ["trade", "test-kitchen-visit"]
  }'

Python

import os
import requests

reply = requests.post(
    "https://api.conecto.chat/v1/tools/crm_save_contact",
    headers={"Authorization": f"Bearer {os.environ['CONECTO_KEY']}"},
    json={
        "email": "jana@weber-kuechenplanung.example",
        "name": "Jana Weber",
        "title": "Kitchen planner",
        "company": "Weber Küchenplanung",
        "phone": "+49 40 66969 120",
        "location": "Hamburg",
        "lifecycle_stage": "lead",
        "tags": ["trade", "test-kitchen-visit"],
    },
)
print(reply.status_code, reply.json())

JavaScript

const reply = await fetch("https://api.conecto.chat/v1/tools/crm_save_contact", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.CONECTO_KEY}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    email: "jana@weber-kuechenplanung.example",
    name: "Jana Weber",
    title: "Kitchen planner",
    company: "Weber Küchenplanung",
    phone: "+49 40 66969 120",
    location: "Hamburg",
    lifecycle_stage: "lead",
    tags: ["trade", "test-kitchen-visit"],
  }),
});
console.log(reply.status, await reply.json());

The reply

JSON
{
  "call_id": "5f0c8a2e-3b7d-4c1e-9a64-2d8e1f7b0c93",
  "tool": "crm_save_contact",
  "status": "succeeded",
  "replayed": false,
  "cost_usd": "$0.01",
  "balance_usd": "$19.75",
  "result": {
    "contact": {
      "id": 4107,
      "email": "jana@weber-kuechenplanung.example",
      "name": "Jana Weber",
      "title": "Kitchen planner",
      "company": "Weber Küchenplanung",
      "company_id": 4105,
      "phone": "+49 40 66969 120",
      "location": "Hamburg",
      "lifecycle_stage": "lead",
      "outreach_status": null,
      "notes": "",
      "tags": ["test-kitchen-visit", "trade"],
      "custom_fields": {},
      "archived": false,
      "updated_at": "2026-10-14T08:00:00+00:00"
    },
    "created": true
  }
}
Field What it is
call_id The call's id, for GET /v1/calls/{call_id} and the call log.
tool The tool that ran.
status succeeded. A background call says running first.
replayed true when the reply repeats an earlier call with the same Idempotency-Key. A replay costs nothing.
cost_usd What this call cost.
balance_usd What the account can spend after it.
result The tool's own answer. Each tool's page shows it.
bought Only on a read that bought the month's next block of reads: what it bought and what that cost.

Amounts are strings in US dollars with as many decimals as they need, at least two, like "$0.01" or "$0.0005".

Over MCP

Connect https://api.conecto.chat/mcp/<area> with the key in the Authorization header, then call tools/call. The idempotency key goes in _meta.idempotencyKey, and an approval in _meta.approvalId.

tools/call, the request
{
  "jsonrpc": "2.0",
  "id": 7,
  "method": "tools/call",
  "params": {
    "name": "crm_save_contact",
    "arguments": {
      "email": "jana@weber-kuechenplanung.example",
      "name": "Jana Weber",
      "title": "Kitchen planner",
      "company": "Weber Küchenplanung",
      "phone": "+49 40 66969 120",
      "location": "Hamburg",
      "lifecycle_stage": "lead",
      "tags": ["trade", "test-kitchen-visit"]
    },
    "_meta": {
      "idempotencyKey": "save-jana-weber"
    }
  }
}
tools/call, the reply
{
  "jsonrpc": "2.0",
  "id": 7,
  "result": {
    "content": [
      {
        "type": "text",
        "text": "{\n \"call_id\": \"5f0c8a2e-3b7d-4c1e-9a64-2d8e1f7b0c93\",\n \"tool\": \"crm_save_contact\",\n \"status\": \"succeeded\",\n \"replayed\": false,\n \"cost_usd\": \"$0.01\",\n \"balance_usd\": \"$19.75\",\n \"result\": {\n  \"contact\": {\n   \"id\": 4107,\n   \"email\": \"jana@weber-kuechenplanung.example\",\n   \"name\": \"Jana Weber\",\n   \"title\": \"Kitchen planner\",\n   \"company\": \"Weber Küchenplanung\",\n   \"company_id\": 4105,\n   \"phone\": \"+49 40 66969 120\",\n   \"location\": \"Hamburg\",\n   \"lifecycle_stage\": \"lead\",\n   \"outreach_status\": null,\n   \"notes\": \"\",\n   \"tags\": [\n    \"test-kitchen-visit\",\n    \"trade\"\n   ],\n   \"custom_fields\": {},\n   \"archived\": false,\n   \"updated_at\": \"2026-10-14T08:00:00+00:00\"\n  },\n  \"created\": true\n }\n}"
      }
    ],
    "structuredContent": {
      "call_id": "5f0c8a2e-3b7d-4c1e-9a64-2d8e1f7b0c93",
      "tool": "crm_save_contact",
      "status": "succeeded",
      "replayed": false,
      "cost_usd": "$0.01",
      "balance_usd": "$19.75",
      "result": {
        "contact": {
          "id": 4107,
          "email": "jana@weber-kuechenplanung.example",
          "name": "Jana Weber",
          "title": "Kitchen planner",
          "company": "Weber Küchenplanung",
          "company_id": 4105,
          "phone": "+49 40 66969 120",
          "location": "Hamburg",
          "lifecycle_stage": "lead",
          "outreach_status": null,
          "notes": "",
          "tags": ["test-kitchen-visit", "trade"],
          "custom_fields": {},
          "archived": false,
          "updated_at": "2026-10-14T08:00:00+00:00"
        },
        "created": true
      }
    },
    "isError": false
  }
}

The reply's structuredContent holds the same envelope as REST, and content holds it as text. A refusal comes back with isError: true and the refusal as its text. Without a key, tools/call answers with a short text that says how to sign up. A batch of up to 50 messages counts each message toward the rate limits. JSON-RPC errors lists what the server itself answers.

What a call costs

Before a call runs, the most it can cost is held against the balance, the key's daily budget, its team's budget and the account's monthly ceiling. When it ends, the account pays what it really cost and the rest of the hold comes back. A call that fails costs nothing, and its whole hold comes back.

Model Tools How it is charged For example
Free 313 Costs nothing. A read counts toward the month's free reads. crm_search: free up to the month's 1,000,000 reads
Per call 219 One price for each call that succeeds. crm_save_contact: $0.01
Per item 40 A price for each thing the call handles, like each record saved or each page read. The most it can cost is held first. crm_save_contacts: $0.01 per record saved
Metered 8 What the work cost, known once it is done, up to a stated most. AI work is charged at the model's cost plus 40% unless the tool says otherwise. email_send: $0.001 per recipient, plus $0.001 per started MB of attachments for each recipient ($0.001 to one person without attachments), up to $0.52
Quoted 1 The price in a quote the tool gives first, up to a stated most. phone_rent_number: quoted, up to $25.00

Reads are free up to 1,000,000 a month for the whole account. Past that, the read that needs more buys the next 10,000 reads for $0.50 before it runs, and its reply says so in bought. Money has the details.

Retrying safely

Send an Idempotency-Key header, any string up to 120 characters, to retry without paying twice. A longer one is kept as its SHA-256. Each idempotency key belongs to the API key that sent it.

  • The same idempotency key with the same tool and arguments, after a success, answers with the first result, replayed: true and a cost of $0.00.
  • After a failure, it runs the call again.
  • While the first call still runs, it answers in_progress with status 409.
  • With another tool or other arguments, it answers idempotency_key_reused with status 409. Use a new idempotency key for a new request.

A tool that returns a secret runs again on a retry, since the call log never keeps the secret.

Background calls

A few tools take a while, like transcribing audio or importing a CRM. They answer 202 at once:

curl

curl -X POST https://api.conecto.chat/v1/tools/speech_transcribe \
  -H "Authorization: Bearer $CONECTO_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "file_id": "7c9e1b3d-5f7a-4c9e-8b1d-3f5a7c9e1b4d",
    "speakers": true,
    "language": "de"
  }'

Python

import os
import requests

reply = requests.post(
    "https://api.conecto.chat/v1/tools/speech_transcribe",
    headers={"Authorization": f"Bearer {os.environ['CONECTO_KEY']}"},
    json={
        "file_id": "7c9e1b3d-5f7a-4c9e-8b1d-3f5a7c9e1b4d",
        "speakers": True,
        "language": "de",
    },
)
print(reply.status_code, reply.json())

JavaScript

const reply = await fetch("https://api.conecto.chat/v1/tools/speech_transcribe", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.CONECTO_KEY}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    file_id: "7c9e1b3d-5f7a-4c9e-8b1d-3f5a7c9e1b4d",
    speakers: true,
    language: "de",
  }),
});
console.log(reply.status, await reply.json());
JSON
{
  "call_id": "9b7e3c51-2f4a-4d8e-a0c6-71e5d2b8f4a3",
  "tool": "speech_transcribe",
  "status": "running",
  "reserved_usd": "$0.60",
  "poll": "GET /v1/calls/9b7e3c51-2f4a-4d8e-a0c6-71e5d2b8f4a3 (or the account_get_call tool) until status is succeeded or failed. You are charged only when it succeeds."
}

Poll GET /v1/calls/{call_id}, or call account_get_call, until status is succeeded or failed. The hold stays until then, and the call is charged only when it succeeds. These tools run in the background: account_export_all, files_ocr, import_from_hubspot, import_from_pipedrive, import_from_zendesk, speech_transcribe and visibility_check.

When a call is refused

A refused call answers with a status of 400 or more and costs nothing. Its error is a code, its message says what to do in words written for an agent, and details, when there is something to add, holds what is needed to fix it.

JSON
{
  "call_id": "5f0c8a2e-3b7d-4c1e-9a64-2d8e1f7b0c93",
  "status": "failed",
  "error": "insufficient_balance",
  "message": "This call costs up to $0.10 and the balance is $0.04. Top up to continue; nothing was charged.",
  "cost_usd": "$0.00",
  "details": {
    "price_usd": "$0.10",
    "balance_usd": "$0.04",
    "top_up": "POST /v1/topups {\"amount_usd\": 20} returns a Stripe checkout link; pay it once and turn on auto-reload so the balance never runs dry.",
    "minimum_usd": 5,
    "fee": "7% + $0.35 per top-up, Stripe's fees passed on",
    "first_topup_offer": "Your first top-up is doubled, up to $20 extra."
  }
}
  • 402 means top up. The details say how.
  • 429 means a limit was reached: a budget, the monthly ceiling or the rate. Limits says which is which.
  • 409 with approval_required means a person must approve this exact call first. Approvals shows the next step.

Errors lists every code with its fix, and each tool's page lists the codes that tool can give.

Limits on a request

  • A body is at most 16 MB. A bigger one answers too_large. Send big files with files_upload_link.
  • Text with the NUL character (U+0000) is refused with invalid_input.
  • Arguments that fail the tool's input schema answer invalid_input, and details.input_schema holds the schema.
  • A tool's secret arguments, like a password, are kept encrypted while the call runs and stored as [redacted] after it.

JSON-RPC errors

An MCP server answers these JSON-RPC errors itself, before any tool runs.

Code When
-32601 Method not found: ...
-32600 Invalid JSON-RPC request.
-32602 Unknown tool ... in this connector. Call tools/list.
-32002 Too many requests. The message names the limit.
-32600 The request is over 16 MB. Send big files with files_upload_link.
-32700 Parse error: the body is not JSON.

Updated . Every route, field and number here is read from the API's own code.