Developers
Connect an AI system
Register an agent, application, model or automation with OpsAI, describe what it can reach, and start sending the events that make it observable.
Two shapes
- Ask
- Your code evaluates and honours the answer. Governs the call sites you instrumented.
- Route
- The action goes through OpsAI. Cannot be bypassed by forgetting.
Pick one deliberately
Asking is a starting point. Routing is the control.
These are not two flavours of the same integration. One depends on your code choosing to ask, and one does not — and the difference decides whether governance survives the next person who adds a code path.
Ask — evaluate, then act
Ten minutes to adopt and it works with anything. The limitation is structural rather than technical: an action taken from a path nobody instrumented is ungoverned, and nothing about the system will tell you that path exists.
const decision = await opsai.actions.evaluate({
agent: 'refund-resolver',
action: 'issue.refund',
subject: 'ORD-40122',
});
if (decision.outcome !== 'authorized') return;
// Your code still performs the action.
await razorpay.refunds.create(…, decision.grant);Route — let OpsAI carry it out
The credential lives with OpsAI, so the action is evaluated on the way through. There is no unguarded path, because the only path to the system requires the decision to have happened.
// No Razorpay credential in your process at all.
const result = await opsai.actions.perform({
agent: 'refund-resolver',
action: 'issue.refund',
subject: 'ORD-40122',
amount: { currency: 'INR', value: 18400 },
});
result.decision; // 'authorized'
result.performed; // true
result.evidence; // 'EV-7512'What OpsAI needs to know
Register what exists, not what you are about to build.
Four fields matter and two of them are commonly misunderstood. Registration describes an AI system that is already running — it does not create, deploy or configure anything.
const agent = await opsai.agents.create({
// Stable identifier. Appears in every trace and every evidence record.
id: 'refund-resolver',
// REQUIRED. One accountable human — not a team, not a rota.
owner: 'priya@acme.internal',
// Descriptive only. Nothing in OpsAI branches on this.
framework: 'langgraph',
model: 'claude-sonnet',
// The actions this system may ATTEMPT. A policy still decides each one.
attempts: ['issue.refund', 'lookup.order'],
// Optional. Where this system sits on the autonomy ladder.
autonomy: 'acts_within_bounds',
});- owner is required
- And cannot be a team. Every question asked after an incident is a question about a person, and a record that cannot name one cannot answer any of them.
- framework changes nothing
- It is recorded because the spread across an estate is worth knowing, not because behaviour varies by it. The 8 frameworks in the sample estate are governed identically.
- attempts is a ceiling, not a grant
- Listing an action means the system may propose it. A policy still decides every individual case, and the connection is a second ceiling above both.
- autonomy can move down on its own
- Breaking a bound drops the system a rung automatically. Coming back up is a decision a person makes, and only once a rule exists that would have caught it.
The system side
A connection holds the credential and declares what it permits.
The permitted-action list is a ceiling rather than a channel: an action absent from it cannot be authorized by any policy, which makes a connection the cheapest way to bound a whole class of mistake.
Connections in the sample estate
12 systems · 11 connected| System | Category | Status | Actions it permits |
|---|---|---|---|
| Razorpay | Payments | Connected | issue.refund · capture.payment |
| RazorpayX | Payouts | Connected | initiate.payout |
| Zoho Books | Accounting | Connected | post.journal_entry |
| NetSuite | ERP | Connected | create.vendor · update.vendor |
| Salesforce | CRM | Connected | update.record |
| Postgres | Database | Connected | adjust.stock · update.row |
| Zendesk | Support | Connected | close.ticket |
| Gmail | Connected | send.email | |
| Slack | Messaging | Connected | notify · request_cosign |
| Amazon S3 | Storage | Connected | export.dataset |
| Box | Documents | Connected | read.contract |
| Workday | HR | Not connected | — |
Systems
12
in the register
Connected
11
credential held by OpsAI
Not connected
1
listed, not hidden
Credentials your code holds
0
grants are derived per action
IllustrativeThe OpsAI sample estate. Workday is unconnected, and stays in the register because an inventory that only lists what works is not an inventory.
const connection = await opsai.connections.create({
system: 'Razorpay',
// Supplied once. No endpoint returns this value afterwards, at any scope.
credential: { kind: 'api_key', value: process.env.RAZORPAY_KEY },
// The ceiling. Nothing outside this list can be authorized.
allows: ['issue.refund', 'payment.read'],
});
// Reading it back never includes the secret.
const read = await opsai.connections.get(connection.id);
read.credential; // { kind: 'api_key', held: true, value: undefined }Where to go
Wire the events next, then tighten the policy.
Once actions route through a connection, the things worth knowing about are the ones your own systems cannot see: a hold waiting on somebody, and a hold that expired because nobody looked.