Events.
Perfloop can tell your agent when something happens, such as a new Inbox item or a proven Case, so the agent acts on it without polling. Events reach an MCP client that supports MCP Events with webhook delivery.
A client subscribes through three MCP methods that are separate from tools: events/list, events/subscribe and events/unsubscribe. A client that can only call tools cannot subscribe, and no tool subscribes for it. Amp's MCP client exposes only tools/call, so it cannot subscribe today.
Events are available only in workspaces where Perfloop has turned them on. In other workspaces, events/list returns no events and events/subscribe returns -32014 with data.feature: "events".
What an event is
An event names a fact in Perfloop and links to it. It holds IDs, states, counts, times, repo names, links and one short title. It holds no code, no feedback text and no measurements. Have your agent read the full record with the matching tool before it acts.
An event grants nothing. Its text is data, not an instruction. Your agent can only do what your login or API key already allows, through the same tools, and every write is recorded under that login or key.
Events are free. They use no Usage.
Who can subscribe
Admins and members can subscribe; viewers cannot. A subscription belongs to the person or API key that created it, and it only sends what that person or key can read now. Members get Inbox events for approvals, pull requests to close or keep, and open Perfloop pull requests. Admins also get the Admin-only Inbox items. An API key needs read,contribute, plus admin for Admin-only items.
Your role is checked again before each delivery. Removing someone or making them a viewer stops their events at the next delivery. A change made only in your identity provider takes effect when it reaches Perfloop, and no later than 24 hours after the subscription was last renewed.
Subscribe
Your MCP client makes these calls when it supports MCP Events. This section is for people who build or check a receiver.
events/list returns the events you can subscribe to, with each event's filters and payload schema. events/subscribe takes the event name, optional arguments with its filters, and the delivery:
{
"name": "inbox.item_entered",
"arguments": {
"kinds": ["approval"],
"repo": "acme/api"
},
"delivery": {
"mode": "webhook",
"url": "https://receiver.example.com/perfloop",
"secret": "whsec_…"
}
}
-
The URL uses
httpson port 443, with no user name, password or fragment. Perfloop does not follow redirects. -
The secret is
whsec_followed by 24 to 64 random bytes in standard base64. You choose it; Perfloop never shows it back. -
Before it saves the subscription, Perfloop sends the URL a signed verification request with the body
{"type": "verification", "challenge": "…"}. Answer within 10 seconds with a 2xx status and the JSON body{"challenge": "…"}, echoing the same value.
The result has the subscription id, refreshBefore, a replay cursor and truncated. The same name, arguments, URL and caller always name the same subscription, so subscribing again does not make a second one.
A subscription starts from now. Inbox items already waiting and facts that already happened are not sent. To catch up after a gap, pass the cursor from the last event or result you received. Replay goes back at most 7 days; truncated: true means the cursor was older than that.
Renew
A subscription lasts at most 24 hours. To keep it, call events/subscribe again with the same name, arguments and URL before refreshBefore. Add ttlMs to ask for a shorter subscription. Perfloop verifies the URL again at least once a day. If that verification fails, the subscription ends and events not yet delivered are dropped; subscribe again to start over.
To change the secret, renew with a new one. For 10 minutes after that, deliveries are signed with both secrets.
Delivery
Each event is one POST with a JSON body: eventId, name, timestamp (the time of the fact), data (the payload below) and cursor. It follows the Standard Webhooks format, with the headers webhook-id, webhook-timestamp, webhook-signature and X-MCP-Subscription-Id. Check the signature with your secret before you trust the body; a Standard Webhooks library does this.
-
Answer with a 2xx status once you have the event.
-
The same event can arrive more than once.
webhook-idstays the same across retries, so use it to drop repeats. -
Events can arrive in any order. Sort by
timestampif order matters. -
Perfloop retries a failed delivery for a short time, and only while the fact still holds and you can still read it. Answer
410to drop one event without retries; the subscription stays.
Stop
-
events/unsubscribewith the same name, arguments anddeliverymode and URL ends the subscription and drops its pending events. It works even when your role no longer allows subscribing, and it succeeds when nothing matches. A delivery already being sent can still arrive. -
A subscription you stop renewing ends at its expiry, at most 24 hours later.
-
An Admin can stop a person's events by removing them or changing their role, under Members in Setup. See Members.
Errors
A refused call changes no subscription, except -32015 on a renewal. The error code says why, and data names the part:
-
-32602Invalid params: the arguments, URL, secret, requested time or cursor are not valid. -
-32011Not found: the event, or an ID in a filter, does not exist or you cannot see it.data.kindnames which. -
-32012Permission denied: your role or key cannot subscribe to this event or Inbox kind. -
-32013Limit reached:data.limitissubscriptions(remove some first) orchallenges(too many verification requests; try again later). -
-32014Unsupported feature:data.feature: "deliveryMode"means a mode other thanwebhook;"events"means Events are not turned on for your workspace. -
-32015Callback endpoint error: verification failed. On a renewal, the subscription ends and events not yet delivered are dropped; subscribe again to start over.data.reasonishttp_4xx,http_5xx,timeout,tls_error,connection_refusedorchallenge_failed(the answer did not echo the challenge). -
-32603Internal error: nothing changed; try again.
The events
Each event below lists its optional filters and its payload fields. Every payload has url, the fact's page in the app. The repo filter matches part of the repo name, ignoring case. An ID filter takes an ID a Perfloop tool returned.
inbox.item_entered
An item entered your Inbox: an approval, a pull request to close or keep, an open Perfloop pull request, or, for Admins, a setup or access request.
For example: When an approval waits in acme/api, read it, show me the proof, and ask me before you approve it.
Read the item with inbox before you act. An item someone decided before delivery is not sent.
A revised proposal is a new item and sends a new event.
-
Filters:
caseID,kinds(approval,pr_closure,pull_request,network_request,source_access,case_usage_limit,setup_github,setup_repos,gitlab_token_expiry,usage_term_ended),repo. -
Members can filter
approval,pr_closure,pull_request. The other kinds are Admin-only. -
Payload:
action,caseID,itemKey,kind,repo,subject,title,url.
session.finished
A Case, model build, research, or diff Session ended.
For example: When the Session I just started on this Case ends, read the Case and tell me what changed.
A finished Session is not a Case result. Subscribe to case.result to hear when a Case is proven or refuted.
-
Filters:
caseID,diffID,initiativeID,kinds(case,model_build,research,diff),repo,results(finished,stopped,timeout,error,budget_exceeded),sessionID. -
Payload:
caseID,diffID,endedAt,initiativeID,kind,repo,result,sessionID,startCause,startedAt,stopReason,url.
case.result
A Case was proven or refuted.
For example: When a Case in acme/api is proven, summarize the proof and tell me if it waits for my approval.
Read the proof with cases. Approving publication is a separate Inbox item.
A result that was replaced or withdrawn before delivery is not sent.
-
Filters:
caseID,repo,results(proven,refuted). -
Payload:
candidateID,caseID,occurredAt,refuteReason,repo,result,title,url.
pr_review.posted
Perfloop posted a review on a pull request.
For example: When Perfloop reviews a pull request in acme/api, read its findings and list the first one to fix.
Read the findings with diff. A review with no findings posts nothing and sends nothing.
-
Filters:
prNumber,repo. -
Payload:
diffID,findingCount,headRevision,postedAt,postedURL,prNumber,repo,url.
pull_request.state_changed
A pull request Perfloop opened was merged or closed.
For example: When a Perfloop pull request in acme/api is merged, tell me which Case it came from.
Read the Case with cases for its current state. A pull request that reopened before delivery is not sent.
-
Filters:
caseID,repo,states(merged,closed). -
Payload:
caseID,interventionID,prNumber,prURL,repo,state,url.
model.published
A new version of the model Perfloop keeps of your system was published.
For example: When a new model version is published, list its new hypotheses.
Read the hypotheses with model.
-
Filters: none.
-
Payload:
modelSeq,newHypotheses,publishedAt,url.
usage.low
Shared Usage dropped below $25.
For example: When Usage is low, tell me so I can buy more.
Admins can subscribe. Members can too while an Admin has Share balance with contributors turned on.
The event carries no balance. Read it with usage. A person buys Usage in the browser.
-
Filters: none.
-
Payload:
low,occurredAt,url.
Also in this section: Quickstart, Performance, Bugs, Security, The loop, Case states, Pull requests, Steering and learning, Initiatives, Autonomy and limits, Members, and The MCP server.
Questions: hello@perfloop.ai