Manage contacts, notes, tags, users, offices and teams in kvCORE / BoldTrail, Inside Real Estate's real-estate CRM.
kvCORE ships in the w6w first-party pack. It declares 26 actions, 2 health checks, and the host runs its code in a sandbox that never sees the credential.
io.w6w.kvcorekvCORE (by Inside Real Estate, now rebranded BoldTrail) is a real-estate CRM and lead-engine platform: brokerages and agent teams run their contact database, lead routing, agent websites and office/team structure through it. This app covers the kvCORE Public API V2 — the API that is actually live today at api.kvcore.com — for contact and lead management (create, read, list, update, tag, and note contacts) and account administration (create, read, list, update and delete users, offices and teams, including team/office membership).
Inside Real Estate is mid-rebrand: its developer hub now documents a second, OAuth-based BoldTrail API (api.boldtrail.com) that its own docs describe as beta and invite-only as of this writing, with no self-serve way to obtain a client. This app deliberately targets the V2 API instead, because it is the one a workflow can actually authenticate against today: any kvCORE user can self-issue a scoped bearer token from their own Lead Dropbox, no partner approval required.
Every endpoint, field and error shape here was verified against the vendor’s own OpenAPI 3.1 document (embedded in developer.insiderealestate.com’s reference pages, fetched 2026-09-29) plus a live, unauthenticated probe against api.kvcore.com on the same day.
Three routes to the same 26 actions. The Workflow tab is generated from kvCORE's own manifest and carries its real ids, so it is copy-pasteable; the Code and CLI examples are the same call for any action on any app, so every app-specific value in them is a blank you fill in.
contact-create Create a new kvCORE contact/lead. Search first — kvCORE permits duplicate contacts and does not de-duplicate for you.
contact-list Search contacts. Combine filters to narrow the result — see the field hints below.
contact-note-add Add a note to a contact. A contact created moments earlier via Create Contact may not be ready to receive a note for up to 30 seconds.
contact-tags-add Add one or more hashtags to a contact. Existing tags are left untouched.
contact-tags-remove Disassociate one or more hashtags from a contact. The tag itself is not deleted from the account. Locked tags can only be removed by an admin-scoped token.
contact-update Update fields on an existing contact. Only the fields you set are changed.
office-update Update fields on an existing office. Only the fields you set are changed.
office-user-add Add an existing user to an office, optionally as an office admin.
office-user-remove Remove a user from an office. Does not delete the user account itself.
team-update Update fields on an existing team. The vendor requires `name` even on update.
team-user-remove Remove a user from a team. Does not delete the user account itself.
user-delete Remove a user from the account. Their leads round-robin to all agents unless you name a reassignment target below.
A workflow step names the app and the action, and the editor fills in the
connection when you pick one. This is the Step shape from the
workflow spec, carrying kvCORE's real ids.
{
"manifestVersion": "2",
"name": "kvcore-example",
"steps": [
{
"id": "contact-create",
"uses": {
"app": "io.w6w.kvcore",
"action": "contact-create",
"connection": "conn_YOUR_CONNECTION_ID"
},
"with": {
"first_name": "<first_name>",
"last_name": "<last_name>",
"email": "<email>"
}
}
]
}contact-create contact-get contact-list contact-note-add contact-tags-add +21 more actions available
Every app-specific value here is a blank you have to fill in. An
app action is reached through the connection that authenticates it, so the
address is a connection id, not the app id — and connections belong to your account,
so a public page cannot know yours. Create one for kvCORE, then fill in
the three blanks: conn_YOUR_CONNECTION_ID, the action key, and the
parameters that action declares. The call itself is real — the shape is transcribed
from the studio's own snippet builder, which prints the same kind of blanks — but
nothing in it is specific to kvCORE. The Workflow tab is where this app's
real ids are.
npm install @w6w/sdkyarn add @w6w/sdkpnpm add @w6w/sdkdeno add npm:@w6w/sdkimport { W6wClient, isActionRun } from "@w6w/sdk";
// Reads W6W_BASE_URL and W6W_TOKEN from the environment when omitted.
const client = new W6wClient();
const envelope = await client.run({
urn: "conn_YOUR_CONNECTION_ID",
action: "contact-create",
payload: {
first_name: "<value>",
last_name: "<value>",
email: "<value>",
// cell_phone_1: "<value>",
// home_phone: "<value>",
// work_phone: "<value>",
// fax: "<value>",
// deal_type: "<value>",
// primary_city: "<value>",
// primary_state: "<value>",
// primary_zip: "<value>",
// primary_address: "<value>",
// status: "<value>",
// source: "<value>",
// email_optin: "<value>",
// phone_on: "<value>",
// text_on: "<value>",
// rating: "<value>",
// capture_method: "<value>",
// assigned_agent_id: "<value>",
// assigned_agent_external_id: "<value>",
// tcpa_optin_date: "<value>",
// avg_price: "<value>",
// avg_beds: "<value>",
// avg_baths: "<value>",
// is_private: "<value>",
// active_mls_id: "<value>",
// spouse_first_name: "<value>",
// spouse_last_name: "<value>",
// spouse_phone: "<value>",
// spouse_title: "<value>",
// title: "<value>",
// birthday: "<value>",
// gender: "<value>",
// poi_address: "<value>",
// poi_city: "<value>",
// poi_state: "<value>",
// poi_zip: "<value>",
// second_email: "<value>",
// external_vendor_id: "<value>",
// last_closing_date: "<value>",
// signup_mlsid: "<value>",
// signup_mls: "<value>",
// owned_by_assigned: "<value>",
// entity_owner_id: "<value>",
// hashtags: "<value>",
},
});
if (isActionRun(envelope)) console.log(envelope.value); npm install -g @w6w/cli w6w run conn_YOUR_CONNECTION_ID --action contact-create --payload '{"first_name":"<value>","last_name":"<value>","email":"<value>"}' Give an AI agent kvCORE — without giving it kvCORE's credentials. One MCP endpoint exposes every app, function and workflow the caller is entitled to, as tools it can discover and run. Access is granted per team while we onboard.
One tool call{
"name": "w6w_invoke",
"arguments": {
"ref": "app:io.w6w.kvcore#contact-create",
"input": {
"first_name": "<first_name>",
"last_name": "<last_name>",
"email": "<email>"
}
}
}
Every tool names its target with a single ref. The
app: form above doesn't name a connection at all — the
host resolves which of the caller's kvCORE connections to sign
with, and refuses rather than guesses when the answer is ambiguous.
The token is attached host-side, at the moment of the call. It is never a tool argument, never in the model's context, and never in a transcript — so a prompt injection has nothing to exfiltrate.
Tools are derived per end user from what that person has actually connected and is entitled to — not one shared bot identity carrying the union of everyone's access.
Multi-step work runs on the workflow engine and returns a run handle the agent can poll — retries, branching and state survive the conversation that started them.
kvCORE's declared health checks are on the surface too, so an agent can tell "the vendor is down" from "your credential expired" before it burns a retry on either.
The MCP surface is part of the hosted platform. kvCORE itself is MIT, and the runtime that executes it is source-available (FSL).
kvCORE declares its own checks, so its health is a property of the app rather than something the host guesses at.
Reads the "kvCORE API" component from status.insiderealestate.com — Inside Real Estate's shared Statuspage across its whole product line, scoped here to just the API component.