ForceEquals SDK — builder integration guide
How to govern your coded agent with ForceEquals. Share this with customers and internal agent authors.
You write the agent. ForceEquals decides whether each step may continue, must pause for a human, or must be blocked.
Your agent reports facts → ForceEquals applies org policy → Your agent obeys
You do not write pause/poll/resume loops. That lives in the SDK.
1. What you install vs what ForceEquals hosts
| Piece | Who runs it | What it is |
|---|---|---|
SDK (pip install forceequals) |
You, inside your agent process | Small Python library |
| ForceEquals API | ForceEquals (or your local mock_server.py) |
Policy + approvals |
| Momentum | ForceEquals web app | Create API key, policies, approve cards |
The SDK is not a server. You do not deploy it. You install it. ForceEquals deploys the API and Momentum.
2. What you need (credentials)
Create these in Momentum while logged in (your email owns the key).
| Item | Example | Where |
|---|---|---|
| API key | fe_live_... |
Momentum → API Key → Generate. Shown once. |
| Agent id | github-agent |
Momentum → Add Agent / Connect. Must match FORCEEQUALS_AGENT_ID. |
Put them in your agent .env (never commit the key):
FORCEEQUALS_API_KEY=fe_live_your_key_here
FORCEEQUALS_AGENT_ID=github-agent
The SDK calls the hosted ForceEquals API. You do not set FORCEEQUALS_BASE_URL. That URL is built into the SDK. (Optional local override: FORCEEQUALS_BASE_URL=http://127.0.0.1:8787 when running mock_server.py.)
You do not send your email in the SDK. The API key is your identity. The API looks up the key and knows which Momentum user owns it.
Not ForceEquals credentials (those stay yours):
- GitHub token, OpenAI/Claude key, database passwords — your agent’s own tools.
If the key is missing, revoked, or not created under your Momentum login, SDK calls return 401.
3. Install
pip install forceequals
Until the package is on PyPI, from this repo:
cd forceequals-platform
pip install -e .
Python 3.10+.
4. Integrate — step by step
Step 1 — Create the client once
from forceequals import (
ForceEquals,
GovernanceBlockedError,
GovernanceRejectedError,
)
fe = ForceEquals() # reads FORCEEQUALS_API_KEY + FORCEEQUALS_AGENT_ID
Step 2 — Wrap each business run with @fe.governed
One GitHub PR, one loan application, one user message = one run.
class MyAgent:
@fe.governed
def handle_event(self, payload: dict) -> None:
...
This allocates execution_id / case_id. Without it, emit_event fails.
Keep your process alive yourself (a while True or worker). ForceEquals only governs this run.
Step 3 — Report facts with emit_event
Call this after something meaningful happened. ForceEquals policies decide continue / pause / block.
decision = fe.emit_event(
"pull_request.opened",
{
"repo": payload["repo"],
"pr": payload["number"],
"title": payload["title"],
"base_branch": payload["base_branch"],
},
)
# If this returns, status was continue (or pause was approved).
# If policy blocked, GovernanceBlockedError is raised.
Do not check policy yourself. Send facts only.
Step 4 — Explicit human gate with request_approval
Use this when you already know a human must approve (merge to main, pay out money). Always pauses.
fe.request_approval(
title=f"Merge PR #{payload['number']} into {payload['base_branch']}",
context={"pr": payload["number"], "repo": payload["repo"]},
)
The SDK waits until a reviewer approves, denies, or requests changes in Momentum (or approve.py locally).
Step 5 — Catch block / reject so the process stays up
def run_one(agent, payload):
try:
agent.handle_event(payload)
except GovernanceBlockedError as exc:
print(f"Blocked by policy: {exc}")
except GovernanceRejectedError as exc:
print(f"Rejected by reviewer: {exc}")
Blocked/rejected stops this run, not the agent process.
Step 6 — Keep doing your work
LLM review, GitHub API, tools — that is your code. After a successful request_approval, continue (comment, merge, notify). ForceEquals does not merge GitHub for you.
5. Minimal example
from forceequals import ForceEquals, GovernanceBlockedError, GovernanceRejectedError
fe = ForceEquals()
class GitHubAgent:
@fe.governed
def handle_pull_request(self, pr: dict) -> None:
fe.emit_event("pull_request.opened", {
"repo": pr["repo"],
"pr": pr["number"],
"title": pr["title"],
})
# your review / tools here
fe.request_approval(
title=f"Merge PR #{pr['number']}",
context={"pr": pr["number"]},
)
print("Governance allowed this merge (you still perform it).")
agent = GitHubAgent()
try:
agent.handle_pull_request({"repo": "acme/app", "number": 42, "title": "Fix"})
except GovernanceBlockedError as exc:
print("Blocked:", exc)
except GovernanceRejectedError as exc:
print("Rejected:", exc)
6. What ForceEquals returns
| Status | Meaning | What the SDK does |
|---|---|---|
continue |
Allowed | Returns; your next line runs |
paused |
Human needed | Waits; card appears in Momentum |
blocked |
Hard no | Raises GovernanceBlockedError |
request_approval always pauses. emit_event is adaptive (policies + Policy LLM).
Optional: a policy may return override_result (forced answer/tool output). The SDK applies that; you do not.
7. Local test (before production URL)
- Log into Momentum locally → create API key → copy
fe_live_...(or useFE_LOCAL_KEYfor a local-only demo). - Start the ForceEquals API:
python mock_server.py(port 8787). - Optional:
FORCEEQUALS_BASE_URL=http://127.0.0.1:8787so afe_live_key hits the local mock instead of the hosted API.FE_LOCAL_KEYalready defaults to localhost. - Run your agent. On pause, approve in Momentum when wired, or:
python approve.py <approval_id>
- Agent should print that it resumed.
8. Checklist
- Momentum account and API key (
fe_live_...) -
FORCEEQUALS_AGENT_IDmatches the agent registered in Momentum -
pip install forceequals -
ForceEquals()once at process start -
@fe.governedon each run -
emit_eventafter real work facts -
request_approvalon irreversible steps - Catch
GovernanceBlockedError/GovernanceRejectedError - Your own loop keeps the agent online for the next event
9. Related
| Doc | Audience |
|---|---|
| PUBLISH_AND_DEPLOY.md | ForceEquals team — PyPI + API hosting |
| FLOW_GUIDE.md | Internal product flow |
| NO_CODE_INTEGRATION.md | n8n / Agentforce (HTTP, no Python SDK) |
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file forceequals-0.1.0.tar.gz.
File metadata
- Download URL: forceequals-0.1.0.tar.gz
- Upload date:
- Size: 18.3 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.9.6
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
8259b9882473bfe1c065b95c67f92d65f1f8201e8471eb9698610de3b93cc66a
|
|
| MD5 |
4a99ef9576489324d87a17cdb878d08d
|
|
| BLAKE2b-256 |
5462baeda61f2c8e480a8f9733cd4da4c97b2b12b0b3b16e54003541a2b49e55
|
File details
Details for the file forceequals-0.1.0-py3-none-any.whl.
File metadata
- Download URL: forceequals-0.1.0-py3-none-any.whl
- Upload date:
- Size: 17.6 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.9.6
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
089e8dcca3b35616862b587d2f813daadc5391bb7855c800c796b86803d0d56c
|
|
| MD5 |
560fe974536f2aeebbd125572a6f8c52
|
|
| BLAKE2b-256 |
7a9cd21fb834482e1526a767f5c8f6c8a2db7ce94f264f7a6258ee1f6bac7333
|