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:

SurfaceForEndpoint
MCP serverAgents (Claude, ChatGPT, custom)/functions/v1/mcp
REST ingestData pipelines, server-side jobs/functions/v1/ingest
Webhooks outGetting paged on eventsyour endpoint

Authentication

Mint an API key in Settings → Developers & agents. Keys start with kep_, are shown once, and carry a scope:

ScopeGrants
readThe six read tools over MCP. Refused by every write and send path.
fullAll 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/mcp

Claude / 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_accountsread

List accounts with their latest health tier, health score (0–100) and ARR, most valuable first.

ParameterTypeDescription
tierstringFilter to one tier: healthy | watch | at_risk | critical.
limitnumberMax accounts to return (default 50).
get_accountread

Full Customer 360 for one account: health score + rationale, open risk flags, subscription and renewal, recent signals, recent agent actions, contacts.

ParameterTypeDescription
accountIdstringAccount UUID.
domainstringAccount domain, e.g. acme.com.
namestringAccount name (exact). Pass any one of the three.
search_accountsread

Search accounts by partial name. Returns id, name, domain, tier and ARR.

ParameterTypeDescription
queryrequiredstringName fragment to match.
money_in_playread

The headline audit: total revenue at risk and ready to grow, plus the top accounts driving each side with the amount and the cited reason.

ParameterTypeDescription
limitnumberTop accounts per side (default 10).
list_actionsread

The action queue: drafted saves and upsells awaiting a human send.

ParameterTypeDescription
statusstringFilter: suggested | awaiting_approval | approved | sent | done | rejected. Default: everything not yet sent.
list_risk_casesread

Open risk cases: account, reason, severity, ARR at risk, and any drafted response ready to execute.

No parameters.

ingest_signalwrite

Push a customer-success signal into the same pipeline the agent scores from. Creates the account by domain if unknown.

ParameterTypeDescription
signalTyperequiredstringe.g. ticket, call, nps, payment, champion_left, note.
summaryrequiredstringHuman-readable summary of the signal.
accountIdstringAccount UUID (or pass accountDomain).
accountDomainstringAccount domain — created on the fly if unknown.
accountNamestringName to use if the account is created from a domain.
sentimentnumber-1 (negative) … 1 (positive).
valuenumberOptional numeric value (score, amount, count).
upsert_accountwrite

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

ParameterTypeDescription
accountIdstringAccount UUID (or pass accountDomain / accountName).
accountDomainstringCompany domain — created on the fly if unknown.
accountNamestringCompany name (used to match or create).
arrCentsnumberAnnual recurring revenue, in cents.
mrrCentsnumberMonthly recurring revenue, in cents.
renewalDatestringRenewal date (ISO, YYYY-MM-DD).
seatsContractednumberContracted seat count.
planstringPlan / tier name.
contractAmountCentsnumberTotal contract value, in cents.
closeDatestringDeal close / signature date (ISO).
contactsarrayPeople on the account: { name, email, title, role? } — role inferred from title if omitted.
draft_savewrite

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

ParameterTypeDescription
accountIdrequiredstringAccount UUID.
draft_upsellwrite

Have the agent draft an expansion email for an account that is ready to grow. Creates a draft in the queue, not sent.

ParameterTypeDescription
accountIdrequiredstringAccount UUID.
run_inspectwrite

Run 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_allwrite

Re-score every account from current signals only (no drafting). Use after a bulk data load. Returns counts.

No parameters.

execute_actionact

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

ParameterTypeDescription
actionIdrequiredstringThe drafted action UUID.
channelstringslack | email | copy | manual. Default: slack.
slackChannelstring#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_upsell land in a queue for human approval.
  • One acting tool. execute_action is 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.jsonMCP server descriptor (name, endpoint, docs). The authoritative tool catalog is the live tools/list.
/openapi.jsonREST ingest API, OpenAPI 3.1.
/llms.txtPlain-text overview for language models.