Three calls. That's it
Your agent keeps its tools, prompts, and LLM. Momentum answers one question: may this step continue, must a person look, or must this case stop?
@fe.governed
One case = one run. This is required before the other two calls.
fe.emit_event(...)
Send what happened. Policy decides continue, pause, or block.
fe.request_approval(...)
Always pauses. The next line runs only after Approve on Feed.
Before you start
Two values from Momentum. Put both in the environment — never in git.
- Register the agent on Feed → Add Agent (Code agent). Copy the agent_id.
- Create a live key on API Key (fe_live_…). It is shown once.
.env
FORCEEQUALS_API_KEY=fe_live_paste_your_key_here
FORCEEQUALS_AGENT_ID=loan-agentInstall
Requires Python 3.10+. The package is forceequals on pypi.org/project/forceequals. Run this in the agent folder.
Terminal
pip install forceequalsQuick start
Create the client, wrap the run, report facts, ask a human when needed. Paste this, swap in your event names, and run it.
agent.py
import os
from forceequals import (
ForceEquals,
GovernanceBlockedError,
GovernanceRejectedError,
)
fe = ForceEquals(
api_key=os.getenv("FORCEEQUALS_API_KEY"),
agent_id=os.getenv("FORCEEQUALS_AGENT_ID"),
)
@fe.governed
def handle_application(payload: dict) -> dict:
fe.emit_event("loan.risk.calculated", {
"loan_amount": payload["amount"],
"risk_score": payload["risk_score"],
})
fe.request_approval(
title="Approve disbursement",
context={"amount": payload["amount"]},
)
# your side-effect after Approve
return {"ok": True}
try:
result = handle_application({"amount": 12000, "risk_score": 0.4})
print("Done", result)
except GovernanceBlockedError as exc:
print("Blocked:", exc.reason)
except GovernanceRejectedError as exc:
print("Rejected:", exc.reason)What each call does
| Call | When to use it |
|---|---|
| @fe.governed | Wrap one business run (one loan, one ticket, one PR). |
| fe.emit_event(name, data) | Report a fact. Policy decides continue, pause, or block. |
| fe.request_approval(title=..., context=...) | Ask a human on Momentum Feed. Your process waits until they resolve it. |
If emit_event is blocked, the SDK raises GovernanceBlockedError. If a pause is rejected, it raises GovernanceRejectedError. Approve resumes the next line.
Review on Feed
- Open Momentum → Feed while the agent is waiting.
- Approve resumes the next line (disburse, merge, send). Reject closes this case. Request change stops this run so you can resubmit.
Every decision is also stored on History.
Environment
| Variable | Purpose |
|---|---|
| FORCEEQUALS_API_KEY | Live key from Momentum (fe_live_…). |
| FORCEEQUALS_AGENT_ID | Same id you registered in Connect Agent. |
| FORCEEQUALS_BASE_URL | Optional override. Defaults to the hosted API — you can omit this. |
| FORCEEQUALS_POLL_SECONDS | How often to check a paused run (default 2). |
You can pass api_key and agent_id in the constructor instead of using env vars.
If something fails
| What you see | What to do |
|---|---|
| No module named forceequals | Run pip install forceequals with the same Python you use to start the agent. |
| 401 / invalid or revoked key | Generate a new key on API Key. Confirm FORCEEQUALS_API_KEY has no extra spaces. |
| execution_id required | Put @fe.governed on the function that handles one case. |
| Nothing appears on Feed | agent_id in code must match Connect Agent. A silent continue will not create a card — pause or fe.request_approval(...) will. |
Built the agent on a canvas instead? Use the no-code guide. To put this org's policies into Cursor, Claude, or Codex — without emitting events — connect on the MCP tab.