Every action your agents take, checked, approved where needed, and provable.
Put one action under control
An AI support agent recommends a $500 customer credit so someone stays. A credit over $100 needs a person. Agentics checks the policy, asks on the phone when it should, and leaves a receipt anyone can check. The numbers below are an example. They are not a measured result.
Allowed
Example
A support agent recommended a $500 customer credit, and a person approved it.
Agent
support-agent · v1 · Example owner
Rule
A customer credit over $100 needs a person. Approved by the owner.
The agent does not issue the credit first. It asks, waits when a person is required, issues the credit in your own billing system, then records what happened.
1
Check the policy
POST /api/policies/evaluate returns allow, warn, require_approval, or block. require_approval is Needs you. This response is the decision. It does not include a new receipt hash.
2
Read the approval
GET /api/approvals/{id} is the approval record: status, the action, and each step.
3
A person decides
POST /api/approvals/{id}/decide is the person's call, from a signed-in session. The agent key cannot make it. A terminal decision seals an outcome receipt when the approval carries a receipt hash. That hash is not in this response.
4
Record the outcome
POST /api/outcomes writes the outcome receipt, including business_impact_usd. The original receipt is not rewritten. A pointer on it names the new receipt.
5
Check the receipt
GET /api/proof/verify recomputes the leaf, the chain, the batch, and the anchor. verified is true only when all four match.
Authentication
Send Authorization: Bearer ak_…. The key is the tenant. These routes also accept a signed-in session cookie. Create the key in the console. It is shown once.
An agent key may check the policy, read an approval, record an outcome, and check a receipt. It may not approve. POST /api/approvals/{id}/decide returns 403 forbidden for an API key, because that role is not an owner, admin, or approver. The person decides at Needs you.
Turn three switches on for the company.
Policy checks use policy_engine. Approvals use approvals_workflow. Receipt checks use proof_mesh. Outcomes use policy_engine as well. If a switch is off, the route returns 404 feature_not_enabled.
An agent key needs policies.read to check a policy or read an approval, policies.write to open an approval, receipts.write to record an outcome, and receipts.read to check a receipt. A key without that scope gets 403 missing_scope. A key for one company cannot read another company's records. Decide stays closed to API keys.
Pass receipt_hash in the policy check: the 64-character hash of the action receipt you already recorded. Re-checking the same hash reuses one open approval. The policy response does not mint that hash. If the hash is not a receipt in this company, the outcome call returns 404 not_found.
Check the policy
Send the action the agent wants to take. In this example a customer credit over $100 matches a rule with on_violation: require_approval, so the response includes approval_id.
{
"decision": "require_approval",
"violations": [
{
"policy_id": "pol_credit_example",
"name": "Customer credits over $100 need a person",
"on_violation": "require_approval",
"severity": "high"
}
],
"approval_id": "6c1d8a20-4b7e-4e1a-9c33-1f0a2b7e4d10"
}
You should see decision and, when a person is required, approval_id. allow and warn mean the agent may continue. block means it must not. A credit of $80 in this example is allow. A credit over $5,000 in the sample stub is block. Your company's own rules decide the live response.
The person approves or denies from Needs you, on the phone or in the console. The HTTP call below is that session, not the agent key. decision is approve or reject. step_index starts at 0.
A rejection returns { "status": "rejected" }. While the company is paused, decide returns 423 tenant_paused and the credit stays held. After an approval, issue the credit in your billing system. Do not issue it before.
Record the outcome
Later, say what the credit did. settled with a positive business_impact_usd means the customer stayed. confirmed_bad with a negative amount means they left. In this example the retained value is $2,400. That figure is tracked on the receipt. It is not a saving caused by Agentics.
Outcome
status
business_impact_usd
Customer stayed Example
settled
$2,400.00
Customer left Example
confirmed_bad
−$2,400.00
Other accepted values are clean, reversed, and false_positive. pending is refused.
curl -s -X POST https://agentics.you/api/outcomes \
-H "Authorization: Bearer $AGENTICS_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"receipt_hash": "7f3a9c1e4b28d0aa91c04e77b6a1d903c4e8f012aa77b6c1d903e4b28d07f3a9",
"status": "settled",
"ref": "billing:cr_0001",
"note": "Customer stayed. Example estimate of retained value $2,400, credit $500.",
"business_impact_usd": 2400
}'
outcome_receipt_hash is a new receipt. The hash you sent is unchanged.
Check the receipt
Anyone with a key for the company can recompute the receipt. The public page is /verify. Anchoring follows on a short delay, so anchor_ok can be false on a receipt that is otherwise intact. verified waits for the anchor.
A self-reported span, such as a step your own SDK posts, is not this check. It stays marked self-reported until Agentics seals it.
Self-reported
Errors
Status
error
When
401
tenant_required
Missing or unknown key, or a session with no company.
400
bad_receipt_hash
Outcome hash is not 64 hex characters.
400
bad_hash
Verify hash is not 64 hex characters.
400
bad_status
Outcome status is not one of the accepted values.
400
bad_business_impact_usd
Impact is present and not a finite number.
400
bad_decision
Decide was not approve or reject.
403
forbidden
The caller cannot decide. API keys always get this.
404
feature_not_enabled
The company's switch for that route is off.
404
not_found
No approval, or no receipt for that hash in this company.
404
step_not_found
That step index is not on the approval.
405
method_not_allowed
Outcomes received a method other than GET or POST.
409
reason string
The outcome could not be sealed.
423
tenant_paused
Pause all is on. A decision cannot release the credit.
Errors are JSON: { "error": "forbidden" }. Some include detail.
Idempotency
Send the same receipt_hash on the policy check. While that approval is still open, the same approval_id comes back. A second check does not open a second queue item.
These routes do not read an Idempotency-Key header. Sending one does not change the response. Posting the outcome again appends another outcome receipt and moves the pointer to it. Send the outcome once.
Decide writes the step again if you call it twice. Treat the first terminal status as final.
Webhook
You can register a URL with POST /api/webhooks. The name approval.pending is on the list of event names. A decision does not send a webhook today, and creating an approval does not either. Poll GET /api/approvals/{id} until the status changes. That is the path the sample uses.
When a decision webhook exists, the body will look like this. It is not sent now.
The support agent is a tool, issue_credit(customer_id, amount_usd, reason), on the OpenAI Agents SDK. It calls the policy check first. On allow, it issues a credit in a local billing module. On Needs you, it prints the approval link and polls. Then it posts the outcome and prints the check URL.
Mock mode runs on this machine. It does not call OpenAI or Agentics.
cd examples/support-credit-openai-agents/typescript
npm install
export OPENAI_API_KEY=...
export AGENTICS_API_KEY=ak_...
node --experimental-strip-types src/agent.ts \
"Customer cus_4192 asked to cancel. Recommend a $500 credit."
cd examples/support-credit-openai-agents
python3 -m venv .venv
source .venv/bin/activate
pip install -r requirements-live.txt
export OPENAI_API_KEY=...
export AGENTICS_API_KEY=ak_...
python run.py --live "Customer cus_4192 asked to cancel. Recommend a $500 credit."
The mock run prints Needs you, then the check URL. Live mode needs only OPENAI_API_KEY and AGENTICS_API_KEY. Do not commit either key.
Each step also reports itself to POST /v1/activity as self-reported, in one run. That route is not live yet. A miss does not stop the credit. Set AGENTICS_ACTIVITY=0 to skip the call. The same tool exists in TypeScript under typescript/src/agent.ts, and as a short graph in langgraph_credit.py.