Keply for agents
Keply exposes its read and act surface to external AI agents 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 → 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://kzqlzstchvmuenukcejb.supabase.co/functions/v1/mcpClaude / ChatGPT: Settings → Connectors → Add custom connector, paste the URL. Claude Code:
claude mcp add --transport http keply https://kzqlzstchvmuenukcejb.supabase.co/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://kzqlzstchvmuenukcejb.supabase.co/functions/v1/mcp",
"headers": { "Authorization": "Bearer kep_YOUR_KEY" }
}
}
}List the tools, then call one:
curl -X POST https://kzqlzstchvmuenukcejb.supabase.co/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://kzqlzstchvmuenukcejb.supabase.co/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. actcontacts 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 the agent 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 the agent 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 the agent 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://kzqlzstchvmuenukcejb.supabase.co/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. Keply POSTs signed JSON; failed deliveries retry with backoff (up to 6 attempts), and an endpoint that keeps failing is disabled.
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. |
/llms.txt | Plain-text overview for language models. |