Announcing CIMD support for MCP Client registration
Learn more

Circleback MCP vs Circleback API for AI Agents (2026)

Saif Ali Shaik
Founding Developer Advocate

TL;DR

  • Circleback's hosted MCP server ships 11 read-only tools covering meetings, transcripts, action items, calendar, email, people, companies, and tags. It cannot write anything.
  • The Circleback API, released August 20, 2026, is the write surface for meetings, action items, and tags. It has no email endpoints.
  • MCP uses OAuth with Dynamic Client Registration (DCR). The API accepts one credential: a user-created cb_ key sent as a bearer token.
  • Both paths share one rate-limit budget per Circleback account: 20 requests per minute on Free, 300 on paid plans.
  • Scalekit handles the credential for both paths, so no Circleback token or key ever touches your agent runtime.

The decision your Circleback agent forces

Your agent needs Circleback context: what a customer said on yesterday's call, which action items are still open, who attended the last three syncs. Circleback now ships two ways to get it. One is a hosted Model Context Protocol (MCP) server that went live in January 2026; the other is a REST API that landed in August. They overlap on reads, split hard on writes and email, and put you on completely different credential models. Here's how to pick.

What Circleback MCP and Circleback API actually are

Both surfaces read the same account data and respect the same meeting permissions. The difference is the consumer each was built for: MCP for AI clients, the API for code you write and control.

Circleback MCP

Circleback launched its MCP server on January 13, 2026. It is a centrally hosted remote server at circleback.ai/api/mcp, maintained by Circleback and served over Streamable HTTP. Authentication is OAuth with DCR: any compliant client registers itself and sends the user through a one-time browser consent. There is no developer app to create first.

The 11 tools are SearchMeetings, ReadMeetings, SearchTranscripts, GetTranscriptsForMeetings, SearchActionItems, SearchCalendarEvents, SearchEmails, FindProfiles, FindCompanies, ListTags, and SearchSupportArticles. Every one is a read. The official reference is the Circleback MCP article in Circleback's support center.

Circleback API

The REST API lives under circleback.ai/api and returns JSON. Its resources are Meetings, Action items, Calendar, Companies, People, and Tags. Unlike MCP, it writes:

  • Meetings: update, import, and delete.
  • Action items: create, update, and delete.
  • Tags: create, update, and delete them, and apply them to or remove them from meetings.

Auth is a single mechanism. The user creates a key under Settings, API keys. The key is shown only once, is formatted cb_<secret>, and is passed as Authorization: Bearer. The official reference is the Circleback API documentation. Free plans include API, MCP, and CLI access, with limited meeting history.

Comparing them where it matters for agents

Four dimensions decide the choice: what each path can do, which auth model it forces on you, what you own in production, and which agent shapes fit each one.

What your agent can actually do

The read surfaces overlap heavily. The gaps are directional: email search and transcript-chunk search exist only on MCP, and every write exists only on the API. The two tables below split reads from writes.

Reads: where the two paths overlap

Capability
Circleback MCP
Circleback API
Search meetings
Yes (SearchMeetings, with date and domain filters)
Yes (GET /search)
Meeting notes, attendees, insights
Yes (ReadMeetings, up to 50 IDs)
Yes (GET /meeting/{meetingId})
Full transcripts
Yes (GetTranscriptsForMeetings, batched)
Yes (GET /meeting/{meetingId}/transcript)
Search action items
Yes, defaults to the user's own
Yes, with scopes like Anyone
Calendar events
Yes (SearchCalendarEvents)
Yes (GET /calendar/events)
People and companies
Yes (FindProfiles, FindCompanies)
Yes (by profile ID or domain)

Writes, email, and events: where they split

Capability
Circleback MCP
Circleback API
Transcript chunk search
Yes (SearchTranscripts)
No dedicated endpoint
Email thread search
Yes (SearchEmails)
No
Recording URL
Not in ReadMeetings output
Yes (recordingUrl)
Create, update, complete, delete action items
No
Yes
Update, import, delete meetings
No
Yes (delete is owner-only)
Create, apply, remove tags
List only (ListTags)
Yes
Push when a meeting finishes
No
Automations webhooks on Pro and above, not REST

Where the gap bites

The write gap is the one that decides architectures. Three agents that cannot run on MCP at all:

  • A follow-up agent that marks action items done once the work ships.
  • A triage agent that tags customer calls.
  • A backfill job that imports meetings from another recorder.

Those agents need PUT /action-item/{actionItemId}, the tag endpoints, and POST /meetings.

The reverse gap is email. SearchEmails queries the mailboxes the user has connected to Circleback, and the API reference has no email resource. A meeting prep agent that wants the last thread with a prospect alongside the last call gets both from one MCP connection. On the API path, email means a second connector.

What MCP makes easier

MCP tools are written for models. Most of them require an intent parameter. SearchMeetings and SearchCalendarEvents use it to return relevant excerpts instead of full records. SearchTranscripts returns matching chunks with start and end timestamps, which is exactly the retrieval step you would otherwise build yourself on top of raw API transcripts.

On the API path, you fetch whole meeting objects and full transcripts, then chunk, rank, and trim them before they enter context. That costs more tokens and more code. In exchange, you control exactly what the model sees.

MCP auth: OAuth with Dynamic Client Registration

Each user signs in to Circleback and consents in a browser once. The resulting token is bound to that user's account and inherits their meeting permissions. Circleback states that users only see meetings they already have access to through MCP.

There is no client ID or secret to provision and no redirect URI to register. For a product onboarding hundreds of users, a consent click is the lowest-friction credential flow available. To understand the mechanics behind this, see the deep dive on Dynamic Client Registration in OAuth2 and its role in agentic auth.

API auth: a static key per user

The API documents one credential: a cb_ key the user creates by hand and copies out of Settings. The API overview describes no scopes, no expiry, and no OAuth option for third-party apps.

A key reads whatever the user can see and writes with the user's authority, including deleting meetings they own. Onboarding therefore means asking every user to generate a secret and paste it into your product. That is a support burden and a security surface at the same time.

What that means for multi-tenant agents

Both paths end in the same place: one Circleback credential per user. MCP hands you an OAuth grant per user; the API hands you a pasted key per user. In neither case does the path itself solve storage, rotation, or revocation. Those are infrastructure problems regardless of which path you choose.

For a detailed comparison of the trade-offs, read OAuth vs API Keys for AI Agents.

Pagination on each path

MCP tools paginate by pageIndex:

  • SearchMeetings: 20 meetings per page.
  • SearchActionItems: 25 action items per page.
  • SearchCalendarEvents: 50 events per page.
  • SearchEmails: 20 threads per mail service per page.

A pageIndex is only valid with the exact same search parameters, so a model that changes a date filter mid-loop breaks its own pagination.

The API uses opaque cursors, returned in an RFC 8288 Link header with rel="next". Cursors cannot be reused with different filters.

Rate limits are shared across both paths

Rate limits are counted per Circleback account, across all API keys and MCP or CLI OAuth tokens:

Plan
Per second
Per minute
Free
3
20
Pro, Business, Enterprise
20
300

Requests over the limit return 429 with a Retry-After header. On Free, your agent shares 20 requests a minute with the user's own Claude or Cursor MCP session.

Maintenance trajectory

On the MCP path, Circleback owns the tool schemas and descriptions. When they change, your agent's tool selection can shift without a redeploy on your side. That is useful, but it is also untested drift.

On the API path, you own request construction, error handling, and retries. The API was only weeks old at the time of writing, and its overview does not describe a versioning scheme. Pin the behavior you depend on with contract tests.

When to use Circleback MCP

The decision tracks what your agent produces, not a preference for one protocol. Use Circleback MCP when:

  • The agent answers questions over meetings in chat, such as "what did Acme push back on in the last two calls?" SearchTranscripts plus ReadMeetings covers it without a retrieval pipeline.
  • Meeting prep needs calendar and email context from one connection: the next event from SearchCalendarEvents, the last thread from SearchEmails, the last call from SearchMeetings.
  • You want zero-registration onboarding, with a DCR consent click instead of pasted secrets.
  • The agent orchestrates across several tools, and model-oriented tool descriptions matter more than raw payloads.

When to use the Circleback API

Use the Circleback API when:

  • The agent writes back: completing action items, reassigning owners, or tagging calls by account or deal stage.
  • You run a deterministic pipeline, such as syncing notes and action items into a warehouse or CRM with cursor pagination and predictable JSON.
  • You need fields MCP does not return, such as recordingUrl, or cross-assignee action item scopes like Anyone and MyWorkspace.
  • You are backfilling meeting history into Circleback with POST /meetings.

The credential problem that exists on both paths

Pick either path and the same infrastructure problem arrives with your second customer.

One credential per user, on either path

Fifty users means fifty Circleback credentials: fifty OAuth grants on MCP, or fifty cb_ keys on the API. Each one must be encrypted at rest, isolated per tenant, never logged, and never placed in the model's context.

Each can also stop working without warning. When a user disconnects the integration or deletes a key, your agent finds out on the next failed call.

The API path adds a sharper edge. A leaked cb_ key is the user's whole account, reads and writes, and nothing in the documented model narrows or time-limits it. For the storage side of this problem, see why a token vault is critical for agent workflows.

Where Scalekit fits

Scalekit's Circleback MCP connector runs the OAuth 2.1 and DCR flow, stores tokens in its vault, and refreshes them. Your agent calls tools by user identifier and never holds a Circleback token.

At the time of writing, the Scalekit catalog lists a Circleback MCP connector only. For the REST path, a custom connector with bearer auth plus Tool Proxy gives you the same model: a Scalekit hosted page collects the user's key, Scalekit stores it, and each request is signed server-side. The MCP vs API decision no longer changes your auth infrastructure.

Building a Circleback agent with Scalekit

Both paths share one runtime model: a connection configured once, a connected account per user, and calls resolved by user identifier. The examples below use Python and LangChain, with a TypeScript version at the end.

Configure the connection and authorize a user

Create a connection for the Circleback MCP connector under AgentKit, Connections in the Scalekit dashboard. These examples use circlebackmcp as the connection name. The string you pass as connection_name must match the dashboard exactly; a mismatch here is the most common integration error.

import os from dotenv import load_dotenv from scalekit import ScalekitClient load_dotenv() scalekit_client = ScalekitClient( env_url=os.getenv("SCALEKIT_ENVIRONMENT_URL"), client_id=os.getenv("SCALEKIT_CLIENT_ID"), client_secret=os.getenv("SCALEKIT_CLIENT_SECRET"), ) actions = scalekit_client.actions CONNECTION = "circlebackmcp" # must match the connection name in the Scalekit dashboard IDENTIFIER = "user_123" # your app's unique ID for this user def ensure_active(connection_name: str, identifier: str) -> None: response = actions.get_or_create_connected_account( connection_name=connection_name, identifier=identifier ) if response.connected_account.status != "ACTIVE": link = actions.get_authorization_link( connection_name=connection_name, identifier=identifier ) print(f"Authorize {connection_name}:", link.link) input("Press Enter after authorizing...") response = actions.get_or_create_connected_account( connection_name=connection_name, identifier=identifier ) if response.connected_account.status != "ACTIVE": raise RuntimeError( f"{connection_name} is {response.connected_account.status}, not ACTIVE" ) ensure_active(CONNECTION, IDENTIFIER)

In production, send the link to the user from your app instead of blocking on input(). The authorize a user guide covers that flow.

Load the scoped Circleback tool surface

The agent should not load a flat connector catalog. list_scoped_tools, which the LangChain adapter calls under the hood, returns only the tools this user's connected account is authorized to call. The tool_names filter then narrows that to the four tools a follow-up agent actually needs. What the user can't do, the agent can't do.

from langchain_openai import ChatOpenAI from langchain_core.messages import HumanMessage, ToolMessage tools = actions.langchain.get_tools( identifier=IDENTIFIER, connection_names=[CONNECTION], tool_names=[ "circlebackmcp_searchmeetings", "circlebackmcp_readmeetings", "circlebackmcp_searchtranscripts", "circlebackmcp_searchactionitems", ], page_size=100, ) tool_map = {t.name: t for t in tools} llm = ChatOpenAI(model="gpt-4o").bind_tools(tools) messages = [ HumanMessage( "What pricing objections came up in my Acme calls this month, " "and which of my action items from those calls are still pending?" ) ] while True: response = llm.invoke(messages) messages.append(response) if not response.tool_calls: print(response.content) break for tc in response.tool_calls: result = tool_map[tc["name"]].invoke(tc["args"]) messages.append(ToolMessage(content=str(result), tool_call_id=tc["id"]))

For a single deterministic call, skip the model and call execute_tool directly:

result = actions.execute_tool( tool_name="circlebackmcp_searchactionitems", connection_name=CONNECTION, identifier=IDENTIFIER, tool_input={ "intent": "List my pending action items", "pageIndex": 0, "status": "PENDING", }, ) print(result.data)

Register the Circleback REST API as a custom connector

Writes need the API. Register it once with the management API as a BEARER connector pointed at Circleback's API base URL. Get $env_access_token from your environment's /oauth/token endpoint using the Client Credentials grant.

curl --fail-with-body --location "$SCALEKIT_ENVIRONMENT_URL/api/v1/custom-providers" \ --header "Authorization: Bearer $env_access_token" \ --header "Content-Type: application/json" \ --data '{ "display_name": "Circleback API", "description": "Circleback REST API: meetings, action items, calendar, people, companies, tags", "auth_patterns": [ { "type": "BEARER", "display_name": "Circleback API key", "description": "Paste an API key from Circleback Settings, API keys", "fields": [ { "field_name": "token", "label": "API key", "input_type": "password", "hint": "Starts with cb_", "required": true } ] } ], "proxy_url": "https://circleback.ai/api", "proxy_enabled": true }'

Next, create a connection for the new connector in the dashboard; this example names it circleback-api. For non-OAuth connectors, the authorization link opens a Scalekit hosted page that collects the key, so the same ensure_active helper works unchanged.

Read and write through Tool Proxy

actions.request() takes a path relative to the connector's proxy_url and returns the raw HTTP response. Scalekit attaches the stored key server-side, so the key never appears in your process.

API_CONNECTION = "circleback-api" # must match your custom connection name ensure_active(API_CONNECTION, IDENTIFIER) # Read: action items assigned to this user (the API defaults to incomplete items) resp = actions.request( connection_name=API_CONNECTION, identifier=IDENTIFIER, path="/action-items", method="GET", query_params={"assigneeType": "Me"}, ) resp.raise_for_status() items = resp.json() # Write: mark the first open item as done if items: update = actions.request( connection_name=API_CONNECTION, identifier=IDENTIFIER, path=f"/action-item/{items[0]['id']}", method="PUT", body={"status": "DONE"}, ) update.raise_for_status() print(update.json())

To let the model decide when to write, wrap these calls in your own LangChain tool functions and bind them alongside the MCP tools. List endpoints paginate by cursor, so pass cursor in query_params on follow-up requests.

The same calls from TypeScript

The Node SDK exposes the same two call shapes: executeTool for the MCP connector and request for Tool Proxy. Save this as agent.mts and run it with npx tsx agent.mts.

import { ScalekitClient } from '@scalekit-sdk/node'; import 'dotenv/config'; const scalekit = new ScalekitClient( process.env.SCALEKIT_ENVIRONMENT_URL!, process.env.SCALEKIT_CLIENT_ID!, process.env.SCALEKIT_CLIENT_SECRET!, ); const actions = scalekit.actions; const identifier = 'user_123'; // MCP path: prebuilt Circleback MCP connector const pending = await actions.executeTool({ connector: 'circlebackmcp', identifier, toolName: 'circlebackmcp_searchactionitems', toolInput: { intent: 'List my pending action items', pageIndex: 0, status: 'PENDING' }, }); console.log(pending.data); // API path: custom connector through Tool Proxy const meetings = await actions.request({ connectionName: 'circleback-api', identifier, path: '/meetings', method: 'GET', }); console.log(meetings);

Why build Circleback agents the Scalekit way

Two capabilities matter most once a Circleback agent leaves the demo: attributable logs for every downstream call, and Virtual MCP for agents that span several tools and tenants.

Tool-call logs attributed to a real user

Every execute_tool and Tool Proxy call runs against a specific connected account. Scalekit's agent logs record who authorized each call, which agent ran it, and the response, and they can be exported to your SIEM.

Meeting data raises the stakes here, because transcripts carry pricing, personnel, and legal conversations. When a security reviewer asks which transcripts the agent read for which user, the answer is a queryable record, not a reconstruction from application logs. See agent tool observability and audit trails for agent auth.

Virtual MCP for multi-tool, multi-tenant agents

Circleback agents rarely stop at Circleback. A follow-up agent reads the call, then posts to Slack. A Virtual MCP Server declares exactly that surface once: which connections, and which tools from each.

One server definition serves every user. Before each run, you mint a short-lived session token scoped to that user's connected accounts. There is no MCP server to deploy, host, or maintain.

from datetime import timedelta from scalekit.actions.models.mcp_config import McpConfigConnectionToolMapping # Setup: once per agent role, not once per user vmcp = actions.mcp.create_config( name="circleback-followup-agent", connection_tool_mappings=[ McpConfigConnectionToolMapping( connection_name="circlebackmcp", tools=[ "circlebackmcp_searchmeetings", "circlebackmcp_readmeetings", "circlebackmcp_searchactionitems", ], ), McpConfigConnectionToolMapping( connection_name="slack", # must match your Slack connection name tools=["slack_send_message"], ), ], ) config_id = vmcp.config.id mcp_server_url = vmcp.config.mcp_server_url # Runtime: before each run, confirm connections and mint a user-scoped token accounts = actions.mcp.list_mcp_connected_accounts( config_id=config_id, identifier=IDENTIFIER, include_auth_link=True ) for account in accounts.connected_accounts: if account.connected_account_status != "ACTIVE": print(f"{account.connection_name} needs auth: {account.authentication_link}") token = actions.mcp.create_session_token( mcp_config_id=config_id, identifier=IDENTIFIER, expiry=timedelta(minutes=30) ).token

Connect the agent to the Virtual MCP Server

LangChain connects through langchain-mcp-adapters, using the static server URL and the per-user token as bearer auth. The endpoint stays the same for every user; only the identity changes.

import asyncio from langchain_mcp_adapters.client import MultiServerMCPClient from langchain_openai import ChatOpenAI from langchain_core.messages import HumanMessage, ToolMessage async def run(): client = MultiServerMCPClient( { "scalekit": { "transport": "streamable_http", "url": mcp_server_url, "headers": {"Authorization": f"Bearer {token}"}, } } ) tools = await client.get_tools() tool_map = {t.name: t for t in tools} llm = ChatOpenAI(model="gpt-4o").bind_tools(tools) messages = [ HumanMessage( "Summarize yesterday's Acme call and post my open action items to #acme-deal" ) ] while True: response = await llm.ainvoke(messages) messages.append(response) if not response.tool_calls: print(response.content) break for tc in response.tool_calls: result = await tool_map[tc["name"]].ainvoke(tc["args"]) messages.append(ToolMessage(content=str(result), tool_call_id=tc["id"])) asyncio.run(run())

Why surface reduction matters here

Circleback's 11 tools plus Slack's full catalog would put dozens of tool definitions into every context window. At roughly 200 tokens each, 40 tools burn 8,000 tokens before the agent does any work, and a model choosing from that many tools picks the wrong one more often.

The Virtual MCP Server above exposes four. The fix is not better prompting; it is surface reduction. For more on this pattern, read when to use a Virtual MCP Server. For a broader look at how tool calling auth patterns evolve as agents move to production, that post covers the full landscape.

Which one to build against

If your agent answers questions over meetings, calendar, and email, start on Circleback MCP. You get 11 model-oriented tools, DCR onboarding, and the only path with email search.

If your agent writes back, the API is the only option: tagging calls, completing action items, importing history, or running a deterministic sync. Many production Circleback agents will use both, with MCP for retrieval and the API for the write-back step. Budget them together, because they share one per-account rate limit.

The credential problem is identical on both paths. That is the part that needs production-grade infrastructure.

Build with the Circleback connector

Start with the Circleback MCP connector docs, browse the Circleback connector page, or scan the full connector library. For the REST path, follow add your own connector.

To start from a working pattern, adapt the meeting prep agent template or the sales call prep agent template. Pricing for agent tool calling is on the AgentKit pricing page. For sibling comparisons, read Granola MCP vs Granola API and Zoom MCP vs Zoom API.

Building a Circleback agent and want a second pair of eyes on the auth model? Use the Talk to us page for immediate help.

No items found.
Agent
Auth Quickstart
On this page
Share this article
Agent
Auth Quickstart

Acquire enterprise customers with
‍zero upfront cost.

Every feature unlocked. No hidden fees.