Base Developers Questions: support@base.ai

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).

  1. Open Settings → Users & team → API tokens and click Generate token.
  2. Give the token a name (no spaces) and, optionally, a comment.
  3. Under Token type choose AI assistant connector.
  4. 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.
  5. 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.
  6. 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>/mcp with the header Authorization: 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>/mcp as the server URL and add a request header Authorization with the value Bearer <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_entry and resends only the rows that did not apply.
  • No email from imports. Importing contacts never invites anyone; only send_invites emails.

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_LIMITED error carrying retryAfterSeconds, and the assistant will usually tell you to try again in a moment. (An HTTP 429 with a Retry-After header 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_advocates with detail: full). list_activities and list_asks have 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.