AI assistant connector
The AI assistant connector lets an AI assistant — Claude, ChatGPT, Cursor, VS Code, Gemini CLI or your own application — read your Base data and answer questions about it in plain language: "Who is on top of the leaderboard?", "How many asks are active?", "Which reference requests are due this month?".
It is read-only unless you allow more. A connector token with no extra permission can look things up and cannot create, change or delete anything in Base, whatever it is asked to do. When you generate the token you can also let the assistant read ask responses, and — if connector write-back is switched on for your company — credit activities, grant points, import and update contacts and send invites. The assistant only ever sees the tools its token allows.
The connector speaks the Model Context Protocol (MCP) over HTTP. If your assistant supports MCP servers over HTTP, it works with Base.
Create a connector link
You need to be a Base admin (company owner or IT role).
- Open Settings → Users & team → API tokens and click Generate token.
- Give the token a name (no spaces) and, optionally, a comment.
- Under Token type choose AI assistant connector.
- Under Permissions choose what the assistant may do (see Permissions). Company data is always on; every other permission is off until you tick it. Leave them all off for a read-only connector.
- Tick the acknowledgement. It names what the token allows — an assistant using this connector can read your advocates (names, emails and consent status), accounts, opportunities, asks, nominations and reference requests, plus whatever you allowed under Make changes, without a review step.
- Click Generate token.
A token's permissions are fixed for its life: rotating keeps them, and to change them you generate a new token and delete the old one. The token list shows them in the Permissions column, and the pill next to the name reads Read only or Read & write. Generating a token records its permissions in the Audit log.
The dialog shows the connector link once. Copy it now; it cannot be shown again (you can always rotate the token to get a new one).
The link has the form:
https://<api host>/mcp/k/<token>
| Environment | <api host> |
|---|---|
| Production | https://prod.customershome.com |
| Staging | https://stage.customershome.com |
The same dialog offers two ways to connect:
- Link (no authentication needed) — the URL above. For chat products that can only take a URL.
- IDE / CLI header — the endpoint
https://<api host>/mcpwith the headerAuthorization: Bearer <token>. For tools that let you set request headers. The dialog gives you a ready-made Claude Code command.
Both forms carry the same token and give the same access. Treat the link like a password: anyone holding it can read your company's data through the connector — and, when the token has a Make changes permission, change it.
Connect your assistant
Claude Code
claude mcp add --transport http base https://<api host>/mcp --header "Authorization: Bearer <token>"
Then in a session, type /mcp to confirm the base server is connected, and ask away.
Cursor
Add to ~/.cursor/mcp.json (or the project's .cursor/mcp.json):
{
"mcpServers": {
"base": {
"url": "https://<api host>/mcp",
"headers": { "Authorization": "Bearer <token>" }
}
}
}
VS Code (GitHub Copilot agent mode)
Add to .vscode/mcp.json:
{
"servers": {
"base": {
"type": "http",
"url": "https://<api host>/mcp",
"headers": { "Authorization": "Bearer <token>" }
}
}
}
Gemini CLI
Add to ~/.gemini/settings.json:
{
"mcpServers": {
"base": {
"httpUrl": "https://<api host>/mcp",
"headers": { "Authorization": "Bearer <token>" }
}
}
}
claude.ai (custom connector)
In claude.ai go to Settings → Connectors → Add custom connector.
- Link variant: paste the connector link
https://<api host>/mcp/k/<token>as the server URL and choose no authentication. The token travels inside the link. - Header variant (beta): use
https://<api host>/mcpas the server URL and add a request headerAuthorizationwith the valueBearer <token>.
Enable the connector in the chat's tools menu; Claude will then call it when a question needs Base data.
ChatGPT (custom connector)
Paste the connector link https://<api host>/mcp/k/<token> as the MCP server URL and choose no
authentication. ChatGPT reaches every tool from deep research and developer mode. Its built-in
search-style discovery (the tools it looks up by the names search and fetch) is not supported yet: the
connector names them search_records and fetch_record, so ask from deep research or developer mode.
Anthropic Messages API (your own application)
Pass the connector as an MCP server on the request. The token goes in authorization_token; the
mcp_toolset entry is required and must name the same server.
{
"model": "claude-opus-5",
"max_tokens": 16000,
"mcp_servers": [
{
"type": "url",
"url": "https://<api host>/mcp",
"name": "base",
"authorization_token": "<token>"
}
],
"tools": [{ "type": "mcp_toolset", "mcp_server_name": "base" }],
"messages": [{ "role": "user", "content": "How many advocates do we have?" }]
}
Send it to POST https://api.anthropic.com/v1/messages with the header anthropic-beta: mcp-client-2025-11-20.
Permissions
| Group | Permission | What the assistant may do |
|---|---|---|
| Read | Company data (always on) | read everything listed under the read tools below |
| Read | Ask responses | read what advocates submitted for an ask (list_ask_responses) |
| Make changes | Credit activities | credit an activity that happened today (trigger_activities) or in the past (import_past_activities) |
| Make changes | Grant points | add or take away points and credits (grant_points) |
| Make changes | Import contacts | create and update contacts by email (import_contacts) or from your CRM (import_crm_contacts), and set their custom attributes (update_advocate_attributes) |
| Make changes | Send invites | email the invitation to join (send_invites) |
Ask responses and Make changes are offered only when connector write-back is switched on for your company. Any Make changes permission shows the warning "An assistant with this link can change data in Base. Every change shows in Connector usage."
What the assistant can do
Every tool calls the Base Public API and nothing else — same checks, same refusals, same field names,
same page-size limits (500 rows per page, or 100 for the reference tools and ask responses; the one exception is
list_custom_attribute_values, which pages the values of one attribute itself, 200 per page). Most tools are a
single call; a few combine the routes named under Reads below (search_records up to nine reads,
fetch_record up to four pages for an ask), and trigger_activities, grant_points and
update_advocate_attributes make one call per row. Every list tool accepts countOnly: true so "how many"
questions do not page through rows. Nothing outside this list is reachable, and a tool whose permission the token
lacks is neither listed nor callable.
Rules for every change
- The assistant asks first. Before every change it says in plain words what will change and for how many rows, and waits for your yes. For invites it lists who will be emailed and waits for a yes in a separate message — never in the same step as an import.
- 1 to 100 rows per call. An empty list or more than 100 rows refuses the whole call. Each row is then checked on
its own: a row Base refuses fails alone and the others still go through. A repeated email in one invite or contact
import call (or a repeated CRM contact id) is sent once, under its first row; each later copy keeps its own line
and fails as a repeat. Importing contacts never invites anyone: a new contact is not invited until
send_invites. - Time and the request limit. Crediting an activity, granting points and updating attributes send one request per row, each counting against the 100 requests a minute, and stop at the limit or after at most 2 minutes (sooner when the connector's short sign-in session is about to run out); rows they did not reach come back as "Not sent". When Base does not answer, the rows of that request come back as "may or may not have been applied" — check them in Base before sending them again. "Today" is Base's date.
- A summary and one line per row. The result counts the rows sent, succeeded and failed, and gives every row its outcome and, when it failed, why (for example "Advocate not found", "Already recorded", "Activity does not exist. Find the API identifier with list_activities."). The assistant reports every failed row, and every past activity imported with "points not synced" (saved, but its points did not reach the leaderboard).
- Retries. Crediting an activity and importing past activities skip what is already recorded, so a retried call
only adds what is missing. Granting points grants again: before retrying, the assistant reads the advocates' points
with
get_advocate_leaderboard_entryand resends only the rows that did not apply. - No email from imports. Importing contacts never invites anyone; only
send_invitesemails.
The assistant picks the tools itself; the reference below is generated from the connector's own tool registry and republished with every change to it, so it is exactly what the assistant can see. "Needs the permission" names the permission a tool needs; the parameter tables give every value rule.
Every tool only reads, except the ones under "Make changes". A tool that needs a permission is offered only to a connector token an admin gave that permission; every other tool is offered to every connector token. "Reads" and "Writes" name the public API routes a tool can call; the parameters are what the assistant may pass.
Discovery
get_company_profile — Get company profile
Return the profile of the company this connector belongs to (currently its name). Use it to confirm the connector works and to learn which company every other result describes.
Reads: GET /api/v1/status
No parameters.
list_activity_types — List activity types
Return every activity type defined for the company: its stable name, whether it needs advocate consent, and the allowed frequencies and classifications. Activity types are the vocabulary other tools use to filter engagement, so read them once before filtering by type name. Nominations carry their own type names (activityTypeName, for example Quote). Filter list_nominations by those, not by this list.
Reads: GET /api/v1/activity-types
No parameters.
describe_custom_attributes — Describe custom attributes
Return the custom attribute definitions of the company grouped by entity: contact (advocate), account, opportunity and nomination. Each definition carries the attribute name used in every other tool output, its label, type, whether advocates can edit it and valueCount, the number of allowed values of an enumerated attribute; the values themselves are listed by list_custom_attribute_values. Narrow with entity and nameContains. Read this before interpreting customAttributes values.
Reads: GET /api/v1/custom-attributes
| Parameter | Type | Required | Description |
|---|---|---|---|
entity |
contact, account, opportunity, nomination |
no | Only this entity (contact = advocate, account, opportunity or nomination); omit for all four |
nameContains |
string | no | Only attributes whose name or label contains this text (case-insensitive); at least 1, at most 200 characters |
describe_custom_objects — Describe custom objects
Return the custom object types (for example subscription, primary_contact) defined in Base for the company, grouped by the entity they attach to: contact (advocate), account, opportunity and nomination; objects synced from the CRM are not part of the public API and are not listed. Each object lists its apiName, label, the attributes its records carry (with valueCount instead of the enumerated values, which list_custom_attribute_values lists) and its CRM mappings. Use the apiName as objectName when reading custom object records. Narrow with entity.
Reads: GET /api/v1/custom-object-mapping, GET /api/v1/custom-object-attributes
| Parameter | Type | Required | Description |
|---|---|---|---|
entity |
contact, account, opportunity, nomination |
no | Only this entity (contact = advocate, account, opportunity or nomination); omit for all four |
list_custom_attribute_values — List custom attribute values
List the allowed values of one enumerated custom attribute, one row per value, paged (at most 200 per page). Definitions from describe_custom_attributes and describe_custom_objects only carry valueCount; use this tool to see the values themselves, narrow them with search, or count them with countOnly.
Reads: GET /api/v1/custom-attributes, GET /api/v1/custom-object-mapping
| Parameter | Type | Required | Description |
|---|---|---|---|
entity |
contact, account, opportunity, nomination |
yes | Entity the attribute is defined on: contact (advocate), account, opportunity or nomination |
name |
string | yes | The attribute name as listed by describe_custom_attributes (or by describe_custom_objects for a custom object attribute); at least 1, at most 200 characters |
customObjectApiName |
string | no | When the attribute belongs to a custom object of the entity: that object's apiName from describe_custom_objects; at least 1 |
search |
string | no | Only values containing this text (case-insensitive); at least 1, at most 200 characters |
page |
integer | no | Zero-based page index; min 0, max 9007199254740991; default 0 |
size |
integer | no | Rows per page, at most 200; min 1, max 200; default 50 |
countOnly |
boolean | no | Return only the total count instead of rows; default false |
list_content_tags — List content tags
List the content tags of the company with how many items carry each tag and the category it belongs to. Filter by a name fragment or by content category; use countOnly for "how many tags" questions.
Reads: GET /api/v1/content-tags
| Parameter | Type | Required | Description |
|---|---|---|---|
name |
string | no | Only tags whose name contains this text; at least 1, at most 200 characters |
contentCategoryUuid |
string | no | Only tags in this content category (uuid from list_content_categories); at least 1, at most 200 characters |
page |
integer | no | Zero-based page index; min 0, max 9007199254740991; default 0 |
size |
integer | no | Rows per page, at most 500; min 1, max 500; default 50 |
countOnly |
boolean | no | Return only the total count instead of rows; default false |
list_content_categories — List content categories
List the content categories of the company with the number of tags in each. Filter by a name fragment; use the category uuid to narrow list_content_tags.
Reads: GET /api/v1/content-categories
| Parameter | Type | Required | Description |
|---|---|---|---|
name |
string | no | Only categories whose name contains this text; at least 1, at most 200 characters |
page |
integer | no | Zero-based page index; min 0, max 9007199254740991; default 0 |
size |
integer | no | Rows per page, at most 500; min 1, max 500; default 50 |
countOnly |
boolean | no | Return only the total count instead of rows; default false |
get_reference_request_form_settings — Get reference request form settings
Return how the reference request form is configured for the company: enabled fields and their labels, contact limits, active reference types, allowed reference managers and the custom attributes it shows. Pass the CRM account, opportunity and reference type as context to see the rules that apply to that combination.
Reads: POST /api/v1/reference-requests/form
| Parameter | Type | Required | Description |
|---|---|---|---|
context |
object | no | CRM account, opportunity and reference type the form is being prepared for; omit for the default configuration |
Advocates
list_users — List users
List the users (advocates) of the company as a basic profile: id, email, name, activation state, registration status and custom attribute values. Pass emails to look specific people up. For account linkage, points, consents and engagement use list_advocates instead. Counts user profiles. For advocate counts use list_advocates.
Reads: GET /api/v1/users
| Parameter | Type | Required | Description |
|---|---|---|---|
emails |
array of string | no | Only users with one of these emails, exactly as stored in Base (at most 50 per call; page through larger lists in batches); at least 1, at most 50 items |
page |
integer | no | Zero-based page index; min 0, max 9007199254740991; default 0 |
size |
integer | no | Rows per page, at most 500; min 1, max 500; default 50 |
countOnly |
boolean | no | Return only the total count instead of rows; default false |
get_user — Get user
Return one user (advocate) of the company by id, with the custom attribute values set on them. Use find_advocate_by_email when you only know the email.
Reads: GET /api/v1/users/{id}
| Parameter | Type | Required | Description |
|---|---|---|---|
id |
string | yes | The user id as returned by list_users (the external auth id, not the email); at least 1 |
list_advocates — List advocates
List the advocates of the company. By default (detail: compact) each row carries the identity and summary fields: uuid, email, name, CRM contact id, account, points, credits, rank, registration status, dates, engagement score total and level; detail: full adds consents, recent activities, custom attribute values, custom object values and the profile image, which makes rows several KB each. Filter to one advocate by uuid or email; emailContains and nameContains match within the requested page (page through with size 500 for an exhaustive search) and, when used, pagination.totalElements is omitted while hasMore keeps its upstream meaning. An email that matches nobody returns an empty page. Use countOnly for "how many advocates" questions (not combinable with the page filters). rankPosition in list rows is 0; the API fills it only when one advocate is read by uuid or email. Use get_advocate_leaderboard_entry or get_leaderboard for ranks. list_advocates counts advocates, list_users counts user profiles and get_leaderboard lists advocates with a leaderboard entry, so their totals differ.
Reads: GET /api/v1/advocate
| Parameter | Type | Required | Description |
|---|---|---|---|
uuid |
string | no | Only the advocate with this uuid; at least 1, at most 200 characters |
email |
string | no | Only the advocate with this email, exactly as stored in Base; at least 1, at most 320 characters |
emailContains |
string | no | Only advocates whose email contains this text (case-insensitive). Matches within the requested page only: the result says how many records it checked and which page to read next; at least 1, at most 320 characters |
nameContains |
string | no | Only advocates whose name ("first last") contains this text (case-insensitive). Matches within the requested page only: the result says how many records it checked and which page to read next; at least 1, at most 200 characters |
detail |
compact, full |
no | compact (default) keeps the identity and summary fields; full returns every documented field and allows at most 100 rows per page; default "compact" |
page |
integer | no | Zero-based page index; min 0, max 9007199254740991; default 0 |
size |
integer | no | Rows per page, at most 500; min 1, max 500; default 50 |
countOnly |
boolean | no | Return only the total count instead of rows; default false |
find_advocate_by_email — Find advocate by email
Return the single advocate with this email. detail: compact (default) returns the identity and summary fields; detail: full adds consents, activities (which can hold the whole history, more than 1,000 entries), custom attribute values and custom object values. The email is matched as stored in Base, so pass it as it appears there. Fails with NOT_FOUND when nobody in the company has that email.
Reads: GET /api/v1/advocate
| Parameter | Type | Required | Description |
|---|---|---|---|
email |
string | yes | Email of the advocate to find, exactly as stored in Base (any non-blank text; the public API does the matching); at least 1, at most 320 characters |
detail |
compact, full |
no | compact (default) keeps the identity and summary fields and drops consents, activities (which can hold the whole history, more than 1,000 entries), custom attribute values, custom object values and the profile image; full returns every documented field; default "compact" |
list_advocates_changed_since — List advocates changed since
List the advocates sourced from one external system that changed at or after a point in time, with their CRM id, points and badges. Meant for incremental sync questions ("who changed since yesterday"). pagination.totalElements counts every advocate that changed; a page only carries those mapped to the given external system, so a page can be empty while the total is not. For advocates without a CRM mapping use list_advocates.
Reads: GET /api/v1/users-by-dates
| Parameter | Type | Required | Description |
|---|---|---|---|
externalSystem |
SalesForce, HubSpot, Oracle, External, Test |
yes | The integration that sourced the advocates (SalesForce, HubSpot, Oracle, External or Test) |
sinceLastModified |
string (date-time) | yes | ISO 8601 date-time with a zone offset or Z, e.g. 2026-05-01T00:00:00Z; only records modified at or after it are returned |
page |
integer | no | Zero-based page index; min 0, max 9007199254740991; default 0 |
size |
integer | no | Rows per page, at most 500; min 1, max 500; default 50 |
countOnly |
boolean | no | Return only the total count instead of rows; default false |
get_advocate_custom_attributes — Get advocate custom attributes
Return the custom attribute values of up to 100 advocates, keyed by advocate uuid. Each entry carries the attribute name, label, type and uuid with the advocate's values (never the attribute's allowed-value list); an advocate with no values maps to an empty list. Use describe_custom_attributes to interpret the attribute names.
Reads: GET /api/v1/advocate/custom-attributes
| Parameter | Type | Required | Description |
|---|---|---|---|
uuids |
array of string | yes | Advocate uuids to read attributes for (1 to 100); at least 1, at most 100 items |
list_advocate_attribute_values_changed_since — List advocate attribute values changed since
List the values of the given custom attributes across advocates that changed at or after a point in time, one row per advocate and attribute with the advocate uuid and CRM id. Meant for incremental sync questions.
Reads: GET /api/v1/advocate/custom-attributes/field-value
| Parameter | Type | Required | Description |
|---|---|---|---|
fieldNames |
array of string | yes | Custom attribute names to read (1 to 50); at least 1, at most 50 items |
sinceLastModified |
string (date-time) | yes | ISO 8601 date-time with a zone offset or Z, e.g. 2026-05-01T00:00:00Z; only records modified at or after it are returned |
page |
integer | no | Zero-based page index; min 0, max 9007199254740991; default 0 |
size |
integer | no | Rows per page, at most 500; min 1, max 500; default 50 |
countOnly |
boolean | no | Return only the total count instead of rows; default false |
list_advocate_custom_object_records — List advocate custom object records
Return the records of one custom object (for example subscription) attached to one advocate, identified by uuid or email, each with its record id and attribute values. Narrow to one record with instanceId.
Reads: GET /api/v1/advocate/custom-object-record/{objectName}
| Parameter | Type | Required | Description |
|---|---|---|---|
objectName |
string | yes | apiName of the custom object (from describe_custom_objects); at least 1 |
advocateUuid |
string | no | uuid of the advocate whose records to read; at least 1, at most 200 characters |
advocateEmail |
string | no | Email of the advocate whose records to read, exactly as stored in Base; at least 1, at most 320 characters |
instanceId |
string | no | Only the record with this instance id; at least 1, at most 200 characters |
Accounts and opportunities
list_accounts — List accounts
List the accounts (customer companies) of the company with domain, size, industry, CRM id, reference status, points and custom attribute values. Pass uuids to read specific accounts; use countOnly for "how many accounts" questions. The API has no name filter. To find an account by name use search_records, or page through with size 500.
Reads: GET /api/v1/account
| Parameter | Type | Required | Description |
|---|---|---|---|
uuids |
array of string | no | Only accounts with one of these uuids (at most 150 per call; read larger sets in batches); at least 1, at most 150 items |
page |
integer | no | Zero-based page index; min 0, max 9007199254740991; default 0 |
size |
integer | no | Rows per page, at most 500; min 1, max 500; default 50 |
countOnly |
boolean | no | Return only the total count instead of rows; default false |
list_account_custom_object_records — List account custom object records
Return the records of one custom object (for example primary_contact) attached to one account, identified by uuid or name, each with its record id and attribute values. Narrow to one record with instanceId.
Reads: GET /api/v1/account/custom-object-record/{objectName}
| Parameter | Type | Required | Description |
|---|---|---|---|
objectName |
string | yes | apiName of the custom object (from describe_custom_objects); at least 1 |
accountUuid |
string | no | uuid of the account whose records to read; at least 1, at most 200 characters |
accountName |
string | no | Name of the account whose records to read; at least 1, at most 200 characters |
instanceId |
string | no | Only the record with this instance id; at least 1, at most 200 characters |
list_opportunities — List opportunities
List the sales opportunities known to the company with stage, amount, probability, close date, account name and custom attribute values. Pass uuids to read specific opportunities; use countOnly for "how many" questions.
Reads: GET /api/v1/opportunity
| Parameter | Type | Required | Description |
|---|---|---|---|
uuids |
array of string | no | Only opportunities with one of these uuids (at most 150 per call; read larger sets in batches); at least 1, at most 150 items |
page |
integer | no | Zero-based page index; min 0, max 9007199254740991; default 0 |
size |
integer | no | Rows per page, at most 500; min 1, max 500; default 50 |
countOnly |
boolean | no | Return only the total count instead of rows; default false |
get_opportunity_by_crm_id — Get opportunity by CRM id
Return one opportunity by its CRM id with stage, amount, probability, close date, account name and its custom attribute values as a name-to-value map. Fails with NOT_FOUND when the CRM id is unknown to the company.
Reads: GET /api/v1/opportunity-attributes/{id}
| Parameter | Type | Required | Description |
|---|---|---|---|
crmId |
string | yes | The opportunity id in the CRM (for example a Salesforce id), not the Base uuid; at least 1 |
Asks and engagement
list_asks — List asks
List the asks (what the company asks its advocates to do) with their type, schedule, trigger and tags. Active and inactive asks are returned alike and the public API offers no filters, so page through and narrow by active, challengeType, tags or dates yourself. Descriptions are plain text. Use countOnly for "how many" questions.
Reads: GET /api/v1/challenge
| Parameter | Type | Required | Description |
|---|---|---|---|
page |
integer | no | Zero-based page index; min 0, max 9007199254740991; default 0 |
size |
integer | no | Rows per page, at most 500; min 1, max 500; default 50 |
countOnly |
boolean | no | Return only the total count instead of rows; default false |
list_ask_responses — List ask responses
Read what advocates submitted for one ask: who responded, the response's status (Completed, Approved, Second approval, Rejected), when it was completed, the points it earned and every answer. Find the ask id with list_asks first. For a survey each answer is a question and its answer; for other asks each submitted field and its value. Answers are plain text and an uploaded file shows by its file name. Up to 100 responses per page; use countOnly for "how many". An ask nobody completed returns an empty page.
Needs the permission: Ask responses (ask_responses:read)
Reads: GET /api/v1/challenge/{uuid}/records
| Parameter | Type | Required | Description |
|---|---|---|---|
askId |
string | yes | The ask id (uuid) from list_asks. One ask per call; at least 1, at most 64 characters |
status |
array of Completed, Approved, Second approval, Rejected |
no | Only responses in these statuses; all four when left out; at least 1, at most 4 items |
completedFrom |
string (date) | no | Only responses completed on or after this date (YYYY-MM-DD) |
completedTo |
string (date) | no | Only responses completed on or before this date (YYYY-MM-DD) |
email |
string | no | Only this advocate's responses (the email as stored in Base); at least 1, at most 320 characters |
page |
integer | no | Zero-based page index; min 0, max 9007199254740991; default 0 |
size |
integer | no | Rows per page, at most 100; min 1, max 100; default 50 |
countOnly |
boolean | no | Return only the total count instead of rows; default false |
list_activities — List activities
List the activities advocates can be credited for (name, description, points, apiIdentifier, category, whether badges are assigned). Paging is the only input: the public API offers no filters for this list, so page through and filter by category or points yourself. Use countOnly for "how many" questions. This is the catalog of activities advocates can earn points for, not a record of what advocates did. No tool returns activity history by date.
Reads: GET /api/v1/activity
| Parameter | Type | Required | Description |
|---|---|---|---|
page |
integer | no | Zero-based page index; min 0, max 9007199254740991; default 0 |
size |
integer | no | Rows per page, at most 500; min 1, max 500; default 50 |
countOnly |
boolean | no | Return only the total count instead of rows; default false |
get_leaderboard — Get leaderboard
Rank advocates by points, highest first, with credits and rank position. Emails are omitted unless includeEmails is true. Page with page and size; the order is stable across pages. Use countOnly for "how many" questions. Points are lifetime totals. rankPosition counts from 1 and matches the Global rank on the advocate's profile. The profile's Company rank is not available.
Reads: GET /api/v1/leaderboard
| Parameter | Type | Required | Description |
|---|---|---|---|
includeEmails |
boolean | no | Include each advocate's email address. Off by default; turn it on only when the answer needs emails; default false |
page |
integer | no | Zero-based page index; min 0, max 9007199254740991; default 0 |
size |
integer | no | Rows per page, at most 500; min 1, max 500; default 50 |
countOnly |
boolean | no | Return only the total count instead of rows; default false |
get_advocate_leaderboard_entry — Get advocate leaderboard entry
Return one advocate's leaderboard entry (points, credits, rank position) by email, without walking the whole leaderboard. Fails with NOT_FOUND when no advocate has that email. Points are lifetime totals. rankPosition counts from 1 and matches the Global rank on the advocate's profile. The profile's Company rank is not available.
Reads: GET /api/v1/leaderboard-by-email
| Parameter | Type | Required | Description |
|---|---|---|---|
email |
string | yes | The advocate's email address, exactly as stored in Base; at least 1, at most 320 characters |
Reference requests and nominations
list_reference_requests — List reference requests
List reference requests (a salesperson asking for a customer reference) with their opportunity, the sourced advocate and the reference record; detail: full adds the advocate history, custom attribute values, custom object values, custom attribute filters and links. Filter by creation date (dateFrom/dateTo), opportunity, reference manager, reference type, account, status, reference record status, idle time or free text. dueFrom/dueTo filter by the deadline within the requested page (page through with size 100 for an exhaustive answer) and, when used, pagination.totalElements is omitted while hasMore keeps its upstream meaning; notes says how many requests the page checked and which page to read next. Use countOnly for "how many" questions (not combinable with dueFrom/dueTo). status takes keys and rows show labels: INITIAL = Registered, SOURCING_A_REFERENCE = Waiting Approval, REJECTED = Rejected, CANCELED = Canceled, REFERENCE_SOURCED = Reference Sourced, OWNER_REJECTED = Contact Owner Rejected, OWNER_APPROVED = Contact Owner Approved, PENDING_APPROVAL = Pending Reference Approval, REFERENCE_REJECTED = Reference Rejected, REFERENCE_PROVIDED = Received Reference, REFERENCE_INTRODUCED = Reference Introduced, REFERENCE_CONTACTED = Reference Contacted, REFERENCE_NOT_CONTACTED = Reference Not Contacted. Open requests, as Base counts them on Home, are every status except REFERENCE_CONTACTED, REJECTED and CANCELED, with referenceRecordStatus [ACTIVE] and includeEmptyReferenceRecordStatus true. With countOnly that gives the Home number.
Reads: GET /api/v1/reference-requests
| Parameter | Type | Required | Description |
|---|---|---|---|
dateFrom |
string (date) | no | Only requests created on or after this date (YYYY-MM-DD) |
dateTo |
string (date) | no | Only requests created on or before this date (YYYY-MM-DD) |
opportunityUuid |
array of string | no | Only requests for these opportunity uuids; at least 1, at most 100 items |
opportunityExternalId |
array of string | no | Only requests for opportunities with these CRM ids; at least 1, at most 100 items |
referenceManagerUuid |
array of string | no | Only requests handled by these reference managers (user uuids); at least 1, at most 100 items |
referenceTypeUuid |
array of string | no | Only requests of these reference types (uuids); at least 1, at most 100 items |
accountUuids |
array of string | no | Only requests whose sourced advocate belongs to these accounts (uuids); at least 1, at most 100 items |
status |
array of INITIAL, SOURCING_A_REFERENCE, REJECTED, CANCELED, REFERENCE_SOURCED, OWNER_REJECTED, OWNER_APPROVED, PENDING_APPROVAL, REFERENCE_REJECTED, REFERENCE_PROVIDED, REFERENCE_INTRODUCED, REFERENCE_CONTACTED, REFERENCE_NOT_CONTACTED |
no | Only requests in these statuses. status takes keys and rows show labels: INITIAL = Registered, SOURCING_A_REFERENCE = Waiting Approval, REJECTED = Rejected, CANCELED = Canceled, REFERENCE_SOURCED = Reference Sourced, OWNER_REJECTED = Contact Owner Rejected, OWNER_APPROVED = Contact Owner Approved, PENDING_APPROVAL = Pending Reference Approval, REFERENCE_REJECTED = Reference Rejected, REFERENCE_PROVIDED = Received Reference, REFERENCE_INTRODUCED = Reference Introduced, REFERENCE_CONTACTED = Reference Contacted, REFERENCE_NOT_CONTACTED = Reference Not Contacted; at least 1 |
referenceRecordStatus |
array of ACTIVE, DONE, CANCELED |
no | Only requests whose reference record is in these statuses; at least 1 |
includeEmptyReferenceRecordStatus |
boolean | no | With referenceRecordStatus: also return requests that have no reference record yet |
searchQuery |
string | no | Free-text search across the request fields; at least 1, at most 200 characters |
minIdleTimeInHours |
integer | no | Only requests idle for at least this many hours; min 0, max 9007199254740991 |
maxIdleTimeInHours |
integer | no | Only requests idle for at most this many hours; min 0, max 9007199254740991 |
dueFrom |
string (date) | no | Only requests whose deadline (UTC date) is on or after this date (YYYY-MM-DD). Matches within the requested page only: the result says how many records it checked and which page to read next |
dueTo |
string (date) | no | Only requests whose deadline (UTC date) is on or before this date (YYYY-MM-DD). Matches within the requested page only: the result says how many records it checked and which page to read next |
detail |
compact, full |
no | compact (default) keeps the identity and summary fields and drops the advocate history, custom attribute values, custom object values, custom attribute filters and links; full returns every documented field; default "compact" |
page |
integer | no | Zero-based page index; min 0, max 9007199254740991; default 0 |
size |
integer | no | Rows per page, at most 100; min 1, max 100; default 50 |
countOnly |
boolean | no | Return only the total count instead of rows; default false |
list_ranked_reference_advocates — List ranked reference advocates
Rank the advocates eligible to give a reference by how well they match weighted criteria (industry, location, seniority, job title, account size, products, use cases, custom attributes, reference score) and return them best match first with their availability and reference history. Every criterion is optional. Use countOnly for "how many" questions. seniorityLevels, geographicalLocations and industryTypes take numeric ids that no tool lists. Rank by jobTitles or searchCriteria text instead.
Reads: POST /api/v1/reference-advocates/report
| Parameter | Type | Required | Description |
|---|---|---|---|
seniorityLevels |
object | no | — |
geographicalLocations |
object | no | — |
industryTypes |
object | no | — |
jobTitles |
object | no | — |
accountSizeFrom |
object | no | — |
accountSizeTo |
object | no | — |
productsAndServices |
object | no | — |
useCases |
object | no | — |
includeGroupedAttributes |
boolean | no | — |
customAttributes |
array of object | no | Weighted rules on custom attributes |
customAttributes[].id |
string | yes | Custom attribute uuid; at least 1, at most 200 characters |
customAttributes[].name |
string | no | at most 200 characters |
customAttributes[].entity |
string | no | The entity the attribute belongs to, e.g. advocate or account; at most 200 characters |
customAttributes[].minValue |
string or number | no | — |
customAttributes[].maxValue |
string or number | no | — |
customAttributes[].values |
array of string | no | — |
customAttributes[].addedBySystem |
boolean | no | — |
customAttributes[].weight |
integer | no | How much this criterion counts in the ranking (default 3); min 0, max 100 |
customObjectAttributes |
object | no | Weighted rules on custom object attributes, keyed by custom object name |
challengeUuid |
string | no | Rank advocates for this ask (uuid); at least 1, at most 200 characters |
advocateUuid |
string | no | Limit the report to this advocate (uuid); at least 1, at most 200 characters |
opportunityId |
string | no | The opportunity the reference is for; at least 1, at most 200 characters |
referenceAdvocateScores |
object | no | — |
hasExternalId |
boolean | no | Only advocates that have an external reference id |
referenceTypeUuid |
string | no | The reference type being sourced (uuid); at least 1, at most 200 characters |
excludedAdvocateUuids |
array of string | no | Advocate uuids to leave out; at least 1, at most 100 items |
availabilityStates |
array of READY, COOLING_DOWN |
no | Only advocates in these availability states; at least 1 |
searchCriteria |
string | no | Free-text search across the advocate fields; at least 1, at most 200 characters |
reportSize |
integer | no | Upper bound on the number of ranked advocates in the report (at most 1000); min 1, max 1000 |
page |
integer | no | Zero-based page index; min 0, max 9007199254740991; default 0 |
size |
integer | no | Rows per page, at most 100; min 1, max 100; default 50 |
countOnly |
boolean | no | Return only the total count instead of rows; default false |
list_nominations — List nominations
List nominations (a salesperson or marketer putting an advocate forward for a case study, reference, review, ...) created in a date range, with their status, comments, the advocate and account, and the ask they came from. dateFrom and dateTo are required and at most 366 days apart. The report is not paged: count is the number of matches and at most 500 are returned; when truncated is true narrow the date range or filters. Repeat nominations are left out unless includeRepeatedNominations is true; the counts on Base's Home include them. Statuses come back as keys, which Base shows as INITIAL = Registered, IN_PROGRESS = In progress, APPROVED = Approved, REJECTED = Rejected, COMPLETED = Completed.
Reads: POST /api/v1/nomination
| Parameter | Type | Required | Description |
|---|---|---|---|
dateFrom |
string (date) | yes | Earliest creation date to include (YYYY-MM-DD) |
dateTo |
string (date) | yes | Latest creation date to include (YYYY-MM-DD) |
statuses |
array of INITIAL, IN_PROGRESS, REJECTED, APPROVED, COMPLETED |
no | Only nominations in these statuses; at least 1 |
marketingAdminUuids |
array of string | no | Only nominations handled by these marketing admins (user uuids); at least 1, at most 100 items |
nominationUuids |
array of string | no | Only these nominations (uuids); at least 1, at most 100 items |
salespersonNames |
array of string | no | Only nominations created by salespeople with these names; at least 1, at most 100 items |
activityTypeNames |
array of string | no | Only nominations whose type name (the activityTypeName on each nomination, for example Quote or Customer Story) is one of these, matched exactly; at least 1, at most 100 items |
searchQuery |
string | no | Free-text search across the nomination fields; at least 1, at most 200 characters |
includeRepeatedNominations |
boolean | no | Also return advocates nominated more than once in the window |
Search
search_records — Search records
Search advocates (by exact email, or by email and name containing the text), accounts (by name) and asks (by title) in one call and return typed ids (advocate:<uuid>, account:<uuid>, ask:<uuid>) with a title and link; pass an id to fetch_record for the full record. Advocates are searched by the API; only the first 2000 accounts and asks are scanned, and truncated is true when more matches may exist: beyond that window, past the time budget, or because the limit was reached first — use the list tools for exhaustive answers. When truncated is true, tell the user the search was partial (notes says how much was searched); do not say the record does not exist.
Reads: GET /api/v1/advocate, GET /api/v1/account, GET /api/v1/challenge
| Parameter | Type | Required | Description |
|---|---|---|---|
query |
string | yes | Free text; an email address finds the advocate with that email; at least 1, at most 200 characters |
limit |
integer | no | Maximum number of results, at most 20; min 1, max 20; default 10 |
fetch_record — Fetch record
Fetch the full record behind a typed id returned by search_records (advocate:<uuid>, account:<uuid> or ask:<uuid>): a title, a plain-text summary, a link and the documented fields as metadata.
Reads: GET /api/v1/advocate, GET /api/v1/account, GET /api/v1/challenge
| Parameter | Type | Required | Description |
|---|---|---|---|
id |
string | yes | A typed id from search_records: advocate:<uuid>, account:<uuid> or ask:<uuid>; at least 1, at most 200 characters |
Make changes
trigger_activities — Trigger activities
Credit advocates for an activity that happened today (Base's date, which can differ from the user's near midnight), with the activity's points, credits, badge and gift and an activity feed entry, as an automated activity would. Find the activity and its apiIdentifier with list_activities first; an activity without an apiIdentifier cannot be triggered, use import_past_activities for it. Before calling, tell the user in plain words what will change and for how many rows, and wait for their yes. Unknown emails fail as "Advocate not found"; no advocate is created. The same advocate and activity on the same day returns "Already recorded", so a retried call only records the rows that are missing. Each row is one request against the token's 100 requests a minute; a call stops at that limit or after at most 2 minutes, and rows it did not reach come back as "Not sent" to send again later. Read the summary in the result and report every failed row with its reason.
Needs the permission: Credit activities (activities:write)
Writes: POST /api/v1/trigger-activity
| Parameter | Type | Required | Description |
|---|---|---|---|
rows |
array of object | yes | The advocates to credit, 1 to 100 per call. Base checks each row on its own: a row it refuses fails alone and the others still go through; at least 1, at most 100 items |
rows[].email |
string | yes | The advocate's email address in Base; at least 1, at most 320 characters |
rows[].activity |
string | yes | The activity's apiIdentifier from list_activities (lower-case letters, digits and underscores) |
rows[].externalEventId |
string | no | Your own id for this occurrence of the event; at least 1, at most 256 characters |
import_past_activities — Import past activities
Credit advocates for activities that already happened, as the Previous activities import does, without a file and without the result email. Find the activity type names with list_activity_types first; the type must exist, and the activity is found by name under it or created there (a name that belongs to another type fails the row). An email not in Base creates the contact, who is not invited. Before calling, tell the user in plain words what will change and for how many rows, and wait for their yes. Say how many contacts will be created. The same advocate, activity and date fails as "Already recorded", whether in Base or earlier in the same call. Read the summary in the result and report every failed row with its reason. Also report every row imported with "points not synced": the activity was saved but its points did not reach the leaderboard, so the user must check that advocate's points.
Needs the permission: Credit activities (activities:write)
Writes: POST /api/v1/activity/import-by-email
| Parameter | Type | Required | Description |
|---|---|---|---|
rows |
array of object | yes | The past activities to credit, 1 to 100 per call. Base checks each row on its own: a row it refuses fails alone and the others still go through; at least 1, at most 100 items |
rows[].email |
string | yes | The advocate's email; an email not in Base creates the contact, without inviting them, and then needs firstName and accountName; at most 127 characters |
rows[].activityType |
string | yes | The activity type, named as list_activity_types names it; at least 1, at most 255 characters |
rows[].activityName |
string | yes | The activity, found by name under that type or created there; at least 1, at most 255 characters |
rows[].date |
string (date) | no | When it happened (YYYY-MM-DD); today by Base's date when left out |
rows[].points |
integer | no | Points to grant; the activity's own points and the same credits when left out; min -2147483647, max 2147483647 |
rows[].credits |
integer | no | Credits to grant; 0 when points are given without credits; min -2147483647, max 2147483647 |
rows[].firstName |
string | no | Required when the email is not in Base yet; ignored otherwise; at most 50 characters |
rows[].lastName |
string | no | Used only when the email is not in Base yet; at most 50 characters |
rows[].accountName |
string | no | Required when the email is not in Base yet; ignored otherwise; at most 255 characters |
grant_points — Grant points
Add or take away points and credits. Points raise an advocate's lifetime points (and can earn points badges); credits raise the balance they can redeem. Leaving credits out grants 0 credits, so ask the user whether the points should also be redeemable and pass credits when they should. Negative numbers take away; neither total may go below 0 and credits may not exceed points, and a row that would break that fails with the reason. The grant shows in the points grant history with the reason and "API". Before calling, tell the user in plain words what will change and for how many rows, and wait for their yes. Unknown emails fail as "Advocate not found". Each row is one request against the token's 100 requests a minute; a call stops at that limit or after at most 2 minutes, and rows it did not reach come back as "Not sent" to send again later. Read the summary in the result and report every failed row with its reason. A retried call grants again: before retrying a call that failed or timed out, read each advocate's points with get_advocate_leaderboard_entry and resend only the rows that were not applied.
Needs the permission: Grant points (points:write)
Writes: PUT /api/v1/points/grant-points
| Parameter | Type | Required | Description |
|---|---|---|---|
rows |
array of object | yes | The grants, 1 to 100 per call. Base checks each row on its own: a row it refuses fails alone and the others still go through; at least 1, at most 100 items |
rows[].email |
string | yes | The advocate's email address in Base; at least 1, at most 320 characters |
rows[].points |
integer | yes | Lifetime points to add; negative takes them away; min -2147483647, max 2147483647 |
rows[].credits |
integer | no | Redeemable credits to add; negative takes them away. 0 when left out; min -2147483647, max 2147483647 |
rows[].reason |
string | yes | Why, shown in the points grant history; at least 1, at most 255 characters |
import_contacts — Import contacts
Create or update contacts by email, as Import contacts does: an existing email updates that contact; a new email creates a contact who is not invited yet (no email is sent), so invite them with send_invites. A new email needs firstName and accountName; an existing contact keeps the ones it has when they are left out or blank, and one without an account yet needs accountName. First and last name change only while the contact is not invited yet: an invited or joined contact keeps their name. An import never moves a contact to another account, and keeps their target crowd. When a name or account was not changed, the row says so in its note, so never tell the user it changed; a saved row can also carry a warning in its note, which you report. A contact who asked to join fails with its reason and nothing changed, as importing them would invite them. Optionally set consents (activity type ids from list_activity_types) and reference pool membership. Never emails anyone; use send_invites for that, separately. Check who already exists with list_advocates first. Before calling, tell the user in plain words what will change and for how many rows, and wait for their yes. Say how many contacts are new and how many will be updated. The same email twice in one call (in any case) is imported once, under its first row; each later copy fails as repeated. Each row reports Created, Updated or Imported (when Base does not say which), with the consents applied and rejected. Read the summary in the result and report every failed row with its reason.
Needs the permission: Import contacts (contacts:write)
Writes: POST /api/v1/advocate/import-by-email
| Parameter | Type | Required | Description |
|---|---|---|---|
rows |
array of object | yes | The contacts to import, 1 to 100 per call. Base checks each row on its own: a row it refuses fails alone and the others still go through; at least 1, at most 100 items |
rows[].email |
string | yes | The contact's email; an existing contact with this email is updated; at most 127 characters |
rows[].firstName |
string | no | Required for a new email; an existing contact keeps theirs when left out or blank; at most 50 characters |
rows[].lastName |
string | no | at most 50 characters |
rows[].accountName |
string | no | The account (company) the contact belongs to. Required for a new email or a contact without an account; a contact who has one stays in it; at most 255 characters |
rows[].consents |
array of object | no | Consents to set on the contact; at least 1, at most 50 items |
rows[].consents[].activityTypeId |
string | yes | The activity type uuid from list_activity_types; at least 1, at most 64 characters |
rows[].consents[].consent |
boolean | yes | Whether the contact agrees to this activity type |
rows[].consents[].frequency |
ANYTIME, ONCE_A_MONTH, ONCE_A_QUARTER, ONCE_A_YEAR, NEVER, PAUSED, REMOVED_FROM_POOL |
no | How often they may be asked, for a type with frequencyEnabled |
rows[].consents[].classification |
PUBLIC_PRIVATE, PUBLIC, PRIVATE |
no | Public, private or both, for a type with classificationEnabled |
rows[].referencePool |
JOIN, REMOVE |
no | Add the contact to the reference pool or take them out of it |
import_crm_contacts — Import contacts from the CRM
Create or update contacts from the company's connected CRM by the CRM's own contact id (for example a Salesforce contact id): Base reads the name, email and account from the CRM. Works only for a company with a connected CRM; an id the CRM does not return fails that row as "Contact not found in the CRM (or the CRM connection needs reconnecting in Base)", and a CRM Base could not read fails every row with "Base could not read the CRM". The same id twice in one call is imported once, under its first row; each later copy fails as repeated. Optionally set consents (activity type ids from list_activity_types) and reference pool membership. Never emails anyone; use send_invites for that, separately. Check who already exists with list_advocates first. Before calling, tell the user in plain words what will change and for how many rows, and wait for their yes. Read the summary in the result and report every failed row with its reason.
Needs the permission: Import contacts (contacts:write)
Writes: POST /api/v1/advocate/import-by-contact-id
| Parameter | Type | Required | Description |
|---|---|---|---|
rows |
array of object | yes | The CRM contacts to import, 1 to 100 per call. Base checks each row on its own: a row it refuses fails alone and the others still go through; at least 1, at most 100 items |
rows[].crmContactId |
string | yes | The contact's id in the connected CRM, e.g. a Salesforce contact id; at least 1, at most 64 characters |
rows[].consents |
array of object | no | Consents to set on the contact; at least 1, at most 50 items |
rows[].consents[].activityTypeId |
string | yes | The activity type uuid from list_activity_types; at least 1, at most 64 characters |
rows[].consents[].consent |
boolean | yes | Whether the contact agrees to this activity type |
rows[].consents[].frequency |
ANYTIME, ONCE_A_MONTH, ONCE_A_QUARTER, ONCE_A_YEAR, NEVER, PAUSED, REMOVED_FROM_POOL |
no | How often they may be asked, for a type with frequencyEnabled |
rows[].consents[].classification |
PUBLIC_PRIVATE, PUBLIC, PRIVATE |
no | Public, private or both, for a type with classificationEnabled |
rows[].referencePool |
JOIN, REMOVE |
no | Add the contact to the reference pool or take them out of it |
update_advocate_attributes — Update contact attributes
Set custom attribute values on contacts by email. Find the attribute names, and which take a single value, with describe_custom_attributes first. The values given replace the current ones; a single-value attribute takes exactly one, and every attribute needs at least one value (this tool never clears one). An unknown attribute name is reported as "Not found" while the others are still saved; a single-value attribute given several values fails the whole row, and nothing in it is saved. An unknown email fails as "Advocate not found". Before calling, tell the user in plain words what will change and for how many rows, and wait for their yes. Each row is one request against the token's 100 requests a minute; a call stops at that limit or after at most 2 minutes, and rows it did not reach come back as "Not sent" to send again later. Read the summary in the result and report every failed row with its reason.
Needs the permission: Import contacts (contacts:write)
Writes: PUT /api/v1/advocate/custom-attributes
| Parameter | Type | Required | Description |
|---|---|---|---|
rows |
array of object | yes | The advocates to update, 1 to 100 per call. Base checks each row on its own: a row it refuses fails alone and the others still go through; at least 1, at most 100 items |
rows[].email |
string | yes | The advocate's email address in Base; at least 1, at most 320 characters |
rows[].attributes |
array of object | yes | at least 1, at most 50 items |
rows[].attributes[].name |
string | yes | The attribute name from describe_custom_attributes; at least 1, at most 100 characters |
rows[].attributes[].values |
array of string | yes | The new values; they replace the current ones. A single-value attribute takes one; at least 1, at most 100 items |
send_invites — Send invites
Email contacts the same invitation as Invite / Re-invite on Users & accounts. A pending contact gets the invite whether or not they were invited before; a contact who already joined is skipped as "Already active"; an unknown email fails as "Contact not found". A repeated email is invited once, under its first row. Find the contacts with list_advocates first. Before calling, tell the user in plain words what will change and for how many rows, and wait for their yes. The emails cannot be taken back, so confirm strictly: list the names and the count of who will be emailed, and call this tool only after the user says yes in a separate message. Never send invites in the same step as an import, even when the user asked for both. Read the summary in the result and report every failed row with its reason.
Needs the permission: Send invites (invites:write)
Writes: POST /api/v1/advocate/invite
| Parameter | Type | Required | Description |
|---|---|---|---|
rows |
array of object | yes | The contacts to invite, 1 to 100 per call. Base checks each row on its own: a row it refuses fails alone and the others still go through; at least 1, at most 100 items |
rows[].email |
string | yes | The contact's email address in Base; at least 1, at most 320 characters |
Limits
- 100 requests per minute per connector token, shared with any other use of the same token on the public API
(full-access API tokens are not rate-limited). Every API request a tool makes counts: crediting an activity,
granting points and updating attributes spend one request per row, so a large call can reach the limit part-way;
its remaining rows then come back as "Not sent" and can be sent again a minute later.
Above the limit a tool call answers a
RATE_LIMITEDerror carryingretryAfterSeconds, and the assistant will usually tell you to try again in a moment. (An HTTP429with aRetry-Afterheader is only answered when the connector itself is asked to open too many sessions — 30 per minute per token.) - Results larger than 1 MB are refused with a readable error asking for a smaller page or narrower filters. Ask the assistant for a smaller page or a count.
- Pages are capped at 500 rows (100 for the reference tools, and 100 for
list_advocateswithdetail: full).list_activitiesandlist_askshave paging only — the assistant filters by category, points or status itself. - A tool call is one public API call, except the few tools above that combine reads or call once per row; each of those requests counts against the limit. The assistant may make several tool calls to answer one question.
- Changes are limited to 100 rows per tool call.
Errors
Errors are readable sentences, never stack traces. 401 and 403 answers also carry a machine-readable code
(additionalInfo.code) so the connector can tell them apart. The ones you may see quoted by the assistant:
| Situation | What the assistant sees |
|---|---|
| Wrong or missing token | 401 — Missing credential or Invalid API token |
| A full-access API token was used | 401 (connector.full_access_token) — This is a full-access API token; create an "AI assistant connector" token instead |
| A tool the token's permissions do not cover | tool error PERMISSION_DENIED naming the permission — an admin can generate a token that has it |
| The token was deactivated, rotated or deleted | 401 (connector.revoked) — This connector is no longer active. Ask an admin in Base for a new link.. A call made through a session opened up to a minute before the change gets the same sentence as a tool error (REVOKED) instead; the call after it gets the 401 |
| The connector is switched off for your company | 403 (connector.not_enabled) — The AI assistant connector is not enabled for this company |
| Too many requests | tool error RATE_LIMITED with retryAfterSeconds; 429 + Retry-After only on the session exchange |
| A filter the API rejects (unknown status, bad date) | INVALID_INPUT with the API's own message |
| A row Base refuses in a change | not an error: that row's outcome is Failed with the reason, and the other rows still go through |
| No such advocate / opportunity | NOT_FOUND with the email or id that was looked up |
| Result over 1 MB | RESULT_TOO_LARGE — request a smaller page or narrower filters |
| Base is temporarily unavailable | 503 — retry later |
Revoking access
Everything happens in the same API tokens table:
- Deactivate the token (status switch) — the connector stops within seconds; you can reactivate later.
- Rotate the token — the old link stops working immediately and a new link is shown once.
- Delete the token — permanent.
Base staff can also switch the connector off for a company; existing connector tokens then stop working immediately and the table shows Disabled by Base next to them.
If a link leaks, rotate the token. A leaked connector token is useless outside the connector: presented
directly to the public API it is refused with This token can only be used through the AI assistant connector.
Usage
Every public API call the assistant makes — including refused ones and every change — is recorded with its tool, API path, status, duration, client name and time, and for a change the rows sent, succeeded and failed (a dash for a read). Rows sent are the rows that reached Base: a repeated row the assistant catches shows as failed in the chat, not in these counts. It shows in the token's Usage drawer (the chart icon on the token row) for the last 180 days. A usage row never holds the arguments or the data returned: no names, emails, points or answers. Calls stopped by the rate limit are not recorded, and under very heavy load a row can be dropped rather than slow the call down.
Each change that went through also adds an Audit log row whose User is the token. Its details name the contacts the change touched, by email (or by CRM contact id for an import by contact id), so the Audit log and its export do hold those emails. Points granted through the connector show in the points grant history with the reason and "API".
Privacy
The connector gives the assistant you connect the same view of your company as a read-only API token: advocate names, emails, consent state, points, custom attributes, accounts and opportunities — plus ask responses when you allow them. That data flows to the assistant provider you connect (Anthropic, OpenAI, Cursor, Google or your own application) under that provider's terms. Connect only assistants your organisation has approved for customer data, and rotate the token when people who knew the link leave.
Questions: support@base.ai.