Make your platform AI-operable.
Call 2,418 named operations through one contract-generated API, with least-privilege scopes, workspace pinning, dry-run validation and replay-safe writes. Subscribe to canonical events through HMAC-signed background webhooks, or build a governed package in the six-stage Connector Builder.
Scoped request in. Signed event out.
POST an action to /api/v1.php with a scoped Bearer key. OpsIQ pins the workspace, validates the action contract and permissions, then executes or dry-runs it. Subscribed canonical events leave through an HMAC-signed delivery queue with traceable attempts and bounded retries.
From key to governed integration in three steps.
Issue a least-privilege key, discover the generated contract, dry-run a write, then subscribe a webhook when your application needs background event delivery.
Get a key
Sign up and generate a scoped Bearer API key (opq_…) in developer settings, and give each integration only the surfaces it needs.
Discover & validate
Read meta.actions, meta.action and meta.scopes; use dry_run to validate writes before execution.
Execute & subscribe
Add an idempotency key for writes, then subscribe your endpoint to the canonical events your application needs.
One connector pattern. Five clean primitives.
Anything platform-specific lives in a connector. The OpsIQ core stays generic, the AI stays predictable, and your integration stays auditable.
Tell OpsIQ what just happened.
Emit canonical events from your platform, or define a governed custom event. Subscribers receive the event through the registered priority and delivery rules.
Event referenceinvoice.paid, ticket.created, subscription.cancelled, customer.signed_up or your own.
Fan-out subscribersThe AI brain, automation rules, mirror connectors and your webhook endpoints all react.
Priority orderDeterministic dispatch so mirrors write before alerts fire.
Tell OpsIQ what the AI is allowed to do.
Every named operation receives a generated contract: scope, feature, read or write kind, risk, confirmation policy, request and response schema, dry-run support, idempotency and stable errors.
Action schema{
"action": "tickets.reply",
"kind": "write",
"scopes": ["tickets.write", "admin"],
"feature": "ticket_system",
"risk": "medium",
"confirmation": "recommended",
"idempotency": "supported",
"dry_run": true,
"request": { "type": "object" },
"response": { "required": ["success"] }
}
Push events to your stack - with cryptographic proof.
Subscribe any URL to any of the 52 deliverable events. OpsIQ POSTs the JSON payload signed with HMAC-SHA256 over the scheme version, the timestamp, the delivery ID and the body together, not the body alone, so yesterday's delivery cannot be replayed at you today.
$raw = file_get_contents('php://input');
$sig = $_SERVER['HTTP_X_OPSIQ_SIGNATURE'] ?? ''; // "v2=<hex>"
$ts = (int)($_SERVER['HTTP_X_OPSIQ_TIMESTAMP'] ?? 0);
$id = (int)($_SERVER['HTTP_X_OPSIQ_DELIVERY'] ?? 0);
if (abs(time() - $ts) > 300) http_response_code(401); // replay window
[$ver, $hex] = array_pad(explode('=', $sig, 2), 2, ''); // split the prefix
$signed = $ver . '.' . $ts . '.' . $id . '.' . $raw; // NOT the body alone
$expected = hash_hmac('sha256', $signed, $secret);
if (!hash_equals($expected, $hex)) http_response_code(401);
$event = json_decode($raw, true); // $id is stable across retries
Use direct HTTP or generate the client you prefer.
The full General API is published as OpenAPI 3.0.3 and Postman. PHP reference senders plus Node and Python integration clients cover signed event and webhook flows; any stack can call the same JSON action surface directly.
const response = await fetch(`${base}/api/v1.php`, {
method: "POST",
headers: {
"Authorization": `Bearer ${process.env.OPSIQ_KEY}`,
"Content-Type": "application/json"
},
body: JSON.stringify({
action: "tickets.reply",
dry_run: true,
idempotency_key: crypto.randomUUID(),
body: "Thanks. We are checking this now."
})
});
$payload = json_encode([
'action' => 'tickets.reply',
'dry_run' => true,
'idempotency_key' => bin2hex(random_bytes(16)),
'body' => 'Thanks - we are checking this now.',
]);
$ch = curl_init($base . '/api/v1.php');
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => $payload,
CURLOPT_HTTPHEADER => [
'Authorization: Bearer ' . getenv('OPSIQ_KEY'),
'Content-Type: application/json',
],
]);
response = requests.post(
f"{base}/api/v1.php",
headers={
"Authorization": f"Bearer {os.environ['OPSIQ_KEY']}",
"Content-Type": "application/json",
},
json={
"action": "tickets.reply",
"dry_run": True,
"idempotency_key": str(uuid.uuid4()),
"body": "Thanks - we are checking this now.",
},
)
Build once. Plug into anything.
A connector is a governed package generated or imported through Identity, Auth, Test, Actions, Triggers and Review. Its manifest declares the capability surfaces OpsIQ is allowed to expose.
Open the six-stage BuilderPlain English in. Audited operation out.
OpsIQ never invents the right call. It walks through the registered contracts, prepares the payload, asks for confirmation when the action requires it, and produces a complete audit row when it executes, so an AI that can act never becomes an AI you can't trust.
API surface at a glance.
One JSON POST surface exposes 2,418 named actions. Each generated contract declares auth scopes, read/write kind, risk, confirmation, request schema, dry-run, idempotency and stable errors.
ACTION tickets.replytickets.writedry_run + idempotencyConfigured / hourACTION security.reportsecurity.writedry_run + idempotencyConfigured / hourACTION platform.routes.writeadminconfirmation + keyConfigured / hourACTION meta.actionsPublic discoveryRead onlyConfigured / hourACTION tickets.listtickets.readRead onlyConfigured / hourACTION connectors.listconnectors.readRead onlyConfigured / hourhr.* contracts · hr.read / hr.write scopesACTION hr.people.listhr.readRead onlyConfigured / hourACTION hr.people.createhr.writedry_run + idempotencyConfigured / hourACTION hr.org.treehr.readRead onlyConfigured / hourhr gets a refusal, not an empty list. What PeopleOS HR is.Rate limits are configured per key on an hourly window. Responses expose X-RateLimit-Limit, X-RateLimit-Remaining and X-RateLimit-Reset; a rejected request returns 429 with Retry-After. Use the OpenAPI 3.0.3 contract or Postman collection generated from the same runtime registries.
Build, test and release in six stages.
Start from a governed profile or define the contract yourself. The Builder validates each stage before it generates a self-contained connector package.
Validate safely, then pass the release gate.
Use dry_run on supported write actions, run the Builder's bounded connection test, and inspect the generated manifest and contract before release. Package validation checks capability names, settings, provenance and required files; background workers handle reactive polling and outbound delivery.
A package cannot claim a capability its code does not have.
OpsIQ\Connectors\ConnectorContract is the authority. Runtime discovery, the Builder, first-party packages, conformance, the General API and every reference read that one registry. Declare a capability and three files have to agree with each other before OpsIQ routes a single row to you.
- 01 connector.php the runtime class
- 02 IdentityProvider.php stable external key
- 03 SalesIngest.php reconcileSales()
- 04 actions.json named operations
- 05 settings.json operator configuration
- 06 connector_contract.json machine-readable authority
hmac-sha256 over a deterministic payload of per-file SHA-256 hashes answers has this been modified since signing. An ed25519 signature answers was it signed by Nabtech, checkable with the bundled public key and no shared secret. Edit one byte and the first stops matching.
marketplace.json is outside the signature by definition: the installer writes it after signing, so the publisher could never have signed it.
A guard that has never failed is unproven.
Green tests tell you nothing broke today. They do not tell you the guard you wrote would stop the thing it was written to stop, because a guard that is never exercised passes exactly like a guard that does nothing. So we stop its heart on purpose, and demand that the monitor screams.
211 /* A read role must never reach a destructive operation. */ 212- if ($isRead && $isDestructive) { 212+ if (false) { 213 $errors[] = "read role '{$role}' points at a destructive op"; 214 return false; 215 }
1[ ok ]hash sha256 4f2a9c1e… remember the file exactly as it is 2[ ok ]control exit 0 green BEFORE anything is touched 3[ ok ]mutate 1 match, lint ok one replacement, then php -l the mutant 4[RED ]prove exit 1 and "read role destructive" IS in the failures 5[ ok ]restore bytes written the original, not a regenerated copy 6[ ok ]verify sha256 matches and the check is green again ══ PROVEN. file restored, hash matched, 1 proof run, 0 failed.
The mutation never applied.
The search text did not match, the file was untouched, the suite passed, and the proof recorded green under the old code about code that was never old. Caught by requiring exactly one textual match and comparing file hashes either side.
The mutation broke something else.
The suite went red because the file stopped parsing or the bootstrap died, and the proof recorded a red that had nothing to do with the guard. Caught by linting the mutant and requiring the named test among the failures.
It cannot leave a mutant on disk.
Every mutation copies the original to a sidecar outside the web-served tree, writes a marker naming the outstanding change, and is undone by a shutdown handler on every exit path. A marker from a crashed run is honoured before the next mutation of that file.
Certification is a command with an exit code.
Not a review queue where somebody reads your code and forms an opinion. Run the same suites we run, on your own machine, before you submit. 8 published JSON Schemas under doc/contracts/schemas/ cover settings, actions, Builder specs, journey and commerce event mappings, workflow recipes, connector contracts and release manifests.
- manifests parse and agree with each other
- capabilities exist on the runtime class
- interfaces implemented where declared
- files every required file present
- actions match the actions manifest
- destructive declare confirmation
- identity a stable external key, not email
- replay a repeat updates, never duplicates
- enterprise declarations match the extension
- docs README, DEVELOPER, CHANGELOG
- schemas every JSON validates
$ php tools/test_connector_conformance_all.php --connector=acme
$ php tools/test_connector_json_schemas.php
$ php tools/verify_commerce_contracts.php
$ php tools/release_gate_connectors.php --require-signatures
Point the first one at a real non-production account with --live-settings=/secure/test-acme.json. While you are still iterating, swap the last one for --unsigned-ok: it treats a missing signature as an expected warning rather than pretending it is fine.
Declaring what you can sell is a file, not a registration.
A connector that can sell declares which of its own operations answer OpsIQ's canonical commerce roles. The presence of the file is the claim. It appears in the selling settings the moment it declares and disappears when it stops. There is no list to join and nobody to ask.
A canonical write role mapped to a generic record write. The match is structural: a storage noun beside a mutating verb, whatever your naming convention. Writing the business row direct skips the platform's pricing, availability locking and confirmation mail, and produces an order the merchant's own system only half believes in. No recovery either, so a timeout would be unrecoverable.
{
"commerce": {
"roles": {
"catalog_search": { "operation": "acme_search_products", "transport": "http" },
"order_get": { "operation": "acme_get_order", "transport": "http" },
"order_create": { "operation": "acme_create_order", "transport": "http",
"recovery": "acme_get_order" }
}
}
}
connector_contract.json is generated and its schema sets additionalProperties:false, so a hand-authored block there would fail validation and then be overwritten on the next generator run.
A role not in CommerceRole does not exist. OpsIQ never infers one from an operation name, because a name is not a promise.
The operation must appear in your actions.json. Declaring one that does not exist promises the customer something that then produces nothing.
The most dangerous timeout is the one after the platform may already have done the work. Without an idempotent "did my write land" lookup, the only options are retry blind or lose the order.
Any payment-class role also declares payment_profile with the environment, webhook_verify using the provider's own scheme, and event_map. An unstated environment is how a test-mode provider takes a live payment.
OpsIQ vs a DIY integration.
What a contract-bound, signed, audited platform gives you that rolling your own webhooks and AI-action plumbing never will.
| Capability | Roll your own | OpsIQ |
|---|---|---|
| Scoped Bearer API + HMAC-signed webhooks | Hand-rolled | ✓ |
| Dry-run writes + 24-hour successful replay | DIY | ✓ |
| 1m → 5m → 30m → 2h → 12h retries | DIY queue | ✓ |
| Action-contract registry (AI can't invent calls) | — | ✓ |
| Confirmation policy before side-effects | — | ✓ |
| Stable error envelope + request IDs | Manual logging | ✓ |
| Direct HTTP + generated client options | Write your own | ✓ |
| OpenAPI 3.0.3 machine-readable contract | Maybe | ✓ |
| Six-stage Builder + package release gate | Build a toolchain | ✓ |
| Workspace-pinned keys and hourly limits | Varies | ✓ |
| Connector pattern: platform code stays isolated | — | ✓ |
Developer questions, answered.
Authentication, contracts, retries, client generation, safe testing and the connector model, before the first production request.
Authorization: Bearer opq_…. Keys can be unrestricted, restricted or read-only, carry least-privilege scopes, obey an hourly limit and can be pinned to a workspace. Webhook signing is separate: outbound deliveries carry X-OpsIQ-Signature, X-OpsIQ-Timestamp, X-OpsIQ-Event and X-OpsIQ-Delivery.hash_hmac('sha256', $rawBody, $secret) and compare its hexadecimal digest with X-OpsIQ-Signature using hash_equals. Validate X-OpsIQ-Timestamp within your replay window and use X-OpsIQ-Delivery as the traceable delivery identifier.dry_run on actions whose generated contract supports it, run the Connector Builder's bounded connection test, inspect its generated files, and pass the package conformance and release gates before treating the connector as ready.settings.json, optional actions.json, a generated capability contract, documentation and optional enterprise extensions. The six-stage Builder assembles it; the registry discovers it; conformance and release gates validate it.