The AI agent for customer success.
Keply for agents
Keply reads your book of accounts, finds the churn early and the expansion before anyone asks, and drafts the move for a person to approve. This page is how your own agent gets at it, over the Model Context Protocol and a REST API. Point Claude, ChatGPT, or a custom orchestrator at one server URL and it can read account health, find the revenue at risk, push signals, and have Keply draft the save, with a human in the loop by default.
Overview
Three surfaces, one credential:
| Surface | For | Endpoint |
|---|---|---|
| MCP server | Agents (Claude, ChatGPT, custom) | /functions/v1/mcp |
| REST ingest | Data pipelines, server-side jobs | /functions/v1/ingest |
| Webhooks out | Getting paged on events | your endpoint |
Authentication
Mint an API key in Settings → Connect an AI tool, which also renders the config for your client with the key already in it. Existing keys are listed, scoped and revoked in Settings → Developers & agents. Keys start with kep_, are shown once, and carry a scope:
| Scope | Grants |
|---|---|
read | The six read tools over MCP. Refused by every write and send path. |
full | All 13 tools plus the REST ingest API. |
Pass the key as a bearer token: Authorization: Bearer kep_…. OAuth sign-in (for one-click connector directories) is coming; the key path stays supported.
Connect over MCP
Transport is Streamable HTTP (JSON-RPC 2.0, protocol 2025-06-18). One stable server URL:
https://api.keply.ai/functions/v1/mcpClaude / ChatGPT: Settings → Connectors → Add custom connector, paste the URL. Claude Code:
claude mcp add --transport http keply https://api.keply.ai/functions/v1/mcp \ --header "Authorization: Bearer kep_YOUR_KEY"
Any other MCP client that takes a JSON config:
{
"mcpServers": {
"keply": {
"type": "streamable-http",
"url": "https://api.keply.ai/functions/v1/mcp",
"headers": { "Authorization": "Bearer kep_YOUR_KEY" }
}
}
}List the tools, then call one:
curl -X POST https://api.keply.ai/functions/v1/mcp \
-H "Authorization: Bearer kep_YOUR_KEY" \
-H "content-type: application/json" \
-d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}'curl -X POST https://api.keply.ai/functions/v1/mcp \
-H "Authorization: Bearer kep_YOUR_KEY" \
-H "content-type: application/json" \
-d '{"jsonrpc":"2.0","id":2,"method":"tools/call",
"params":{"name":"money_in_play","arguments":{"limit":5}}}'Tool reference
13 tools. read never modifies anything. write moves data in or creates drafts, but never reaches a customer. act contacts a customer and obeys the account's autonomy dial. Every tool carries MCP behavioral annotations (readOnlyHint / destructiveHint) that match this table.
list_accountsreadList accounts with their latest health tier, health score (0–100) and ARR, most valuable first.
| Parameter | Type | Description |
|---|---|---|
tier | string | Filter to one tier: healthy | watch | at_risk | critical. |
limit | number | Max accounts to return (default 50). |
get_accountreadFull Customer 360 for one account: health score + rationale, open risk flags, subscription and renewal, recent signals, recent agent actions, contacts.
| Parameter | Type | Description |
|---|---|---|
accountId | string | Account UUID. |
domain | string | Account domain, e.g. acme.com. |
name | string | Account name (exact). Pass any one of the three. |
search_accountsreadSearch accounts by partial name. Returns id, name, domain, tier and ARR.
| Parameter | Type | Description |
|---|---|---|
queryrequired | string | Name fragment to match. |
money_in_playreadThe headline audit: total revenue at risk and ready to grow, plus the top accounts driving each side with the amount and the cited reason.
| Parameter | Type | Description |
|---|---|---|
limit | number | Top accounts per side (default 10). |
list_actionsreadThe action queue: drafted saves and upsells awaiting a human send.
| Parameter | Type | Description |
|---|---|---|
status | string | Filter: suggested | awaiting_approval | approved | sent | done | rejected. Default: everything not yet sent. |
list_risk_casesreadOpen risk cases: account, reason, severity, ARR at risk, and any drafted response ready to execute.
No parameters.
ingest_signalwritePush a customer-success signal into the same pipeline Keply scores from. Creates the account by domain if unknown.
| Parameter | Type | Description |
|---|---|---|
signalTyperequired | string | e.g. ticket, call, nps, payment, champion_left, note. |
summaryrequired | string | Human-readable summary of the signal. |
accountId | string | Account UUID (or pass accountDomain). |
accountDomain | string | Account domain, created on the fly if unknown. |
accountName | string | Name to use if the account is created from a domain. |
sentiment | number | -1 (negative) … 1 (positive). |
value | number | Optional numeric value (score, amount, count). |
upsert_accountwriteCreate or update an account and its commercial truth from any CRM or billing tool. Matched by id, domain, or name; hand-set fields are never overwritten. The account is re-scored right away.
| Parameter | Type | Description |
|---|---|---|
accountId | string | Account UUID (or pass accountDomain / accountName). |
accountDomain | string | Company domain, created on the fly if unknown. |
accountName | string | Company name (used to match or create). |
arrCents | number | Annual recurring revenue, in cents. |
mrrCents | number | Monthly recurring revenue, in cents. |
renewalDate | string | Renewal date (ISO, YYYY-MM-DD). |
seatsContracted | number | Contracted seat count. |
plan | string | Plan / tier name. |
contractAmountCents | number | Total contract value, in cents. |
closeDate | string | Deal close / signature date (ISO). |
contacts | array | People on the account: { name, email, title, role? }, role inferred from title if omitted. |
draft_savewriteHave Keply draft a retention save email for an at-risk account. Creates a draft in the action queue; it is NOT sent; a human approves and sends.
| Parameter | Type | Description |
|---|---|---|
accountIdrequired | string | Account UUID. |
draft_upsellwriteHave Keply draft an expansion email for an account that is ready to grow. Creates a draft in the queue, not sent.
| Parameter | Type | Description |
|---|---|---|
accountIdrequired | string | Account UUID. |
run_inspectwriteRun the full inspect loop now: re-score every account, raise risk flags, draft a save for each at-risk account. Returns a summary of what changed.
No parameters.
rescore_allwriteRe-score every account from current signals only (no drafting). Use after a bulk data load. Returns counts.
No parameters.
execute_actionactExecute a drafted action through a channel and log it to the outcome ledger. The only tool that contacts a customer; it obeys the account's autonomy dial.
| Parameter | Type | Description |
|---|---|---|
actionIdrequired | string | The drafted action UUID. |
channel | string | slack | email | copy | manual. Default: slack. |
slackChannel | string | #channel or @user; blank DMs the connected user. |
REST ingest API
POST /functions/v1/ingest with a batch of events: account, usage, or signal. Accounts are created by domain on the fly; every touched account is re-scored inline. Needs a full-scope key. Full schema in openapi.json.
curl -X POST https://api.keply.ai/functions/v1/ingest \
-H "Authorization: Bearer kep_YOUR_KEY" \
-H "content-type: application/json" \
-d '{"events":[
{"type":"account","accountDomain":"acme.com","arrCents":1200000,
"renewalDate":"2026-01-31","seatsContracted":25},
{"type":"usage","accountDomain":"acme.com","event":"weekly_active","trend":-0.4},
{"type":"signal","accountDomain":"acme.com","signalType":"ticket",
"summary":"P1 outage","sentiment":-0.6}
]}'Webhooks
Register an HTTPS endpoint in Settings → Developers & agents → Webhooks and pick events: risk.raised, score.changed, renewal.due, outreach.composed. Keply POSTs signed JSON; failed deliveries retry with backoff (up to 6 attempts), and an endpoint that keeps failing is disabled.
The first three are signals. outreach.composed carries the finished subject and body, and it fires when you switch delivery to handoff: Keply decides who to write to and what to say, and your own system puts it in front of the customer. Direct is the default, so nothing changes until you turn handoff on.
POST https://your-app.com/hooks/keply
X-Keply-Event: risk.raised
X-Keply-Delivery: d1f0…
X-Keply-Signature: sha256=<hmac-sha256 of the raw body>
{
"id": "d1f0…",
"event": "risk.raised",
"timestamp": "2026-07-09T12:00:00Z",
"data": {
"account": { "id": "…", "name": "Acme", "domain": "acme.com" },
"reason": "champion_left",
"severity": "high",
"arrAtRiskCents": 1200000,
"detectedAt": "2026-07-09T11:58:00Z"
}
}Verify X-Keply-Signature, an HMAC-SHA256 of the raw body keyed by the endpoint's signing secret (shown once at creation):
const crypto = require('crypto');
function verify(secret, rawBody, sig) {
const expected = 'sha256=' +
crypto.createHmac('sha256', secret).update(rawBody).digest('hex');
return crypto.timingSafeEqual(Buffer.from(sig), Buffer.from(expected));
}Safety & autonomy
- Drafts never auto-send.
draft_save/draft_upsellland in a queue for human approval. - One acting tool.
execute_actionis the only tool that contacts a customer, and it obeys the per-account autonomy dial (observe → draft → act). - Least privilege. Read-only keys are refused at every write and send path.
- Audit log. Every agent action is recorded with identity, timestamp, and result.
- Transparent schemas. The tool descriptions your agent receives match this page: no hidden instructions or undisclosed side effects. The MCP only accepts tokens issued for it.
Spec files
/.well-known/mcp.json | MCP server descriptor (name, endpoint, docs). The authoritative tool catalog is the live tools/list. |
/openapi.json | REST ingest API, OpenAPI 3.1. |