Build on controlled execution
A versioned REST API, an OpenAPI 3.1 description, a TypeScript SDK and an MCP endpoint, all on the same lifecycle.
Concepts
- Tool — a versioned contract: input schema, operation, risk class (
read,low_write,sensitive_write,high_impact), reversibility, idempotency and default postconditions. - Connected account — credentials for one system in one environment (
test,sandbox,production). Secrets are write-only. - Run — a single action or a workflow execution. Statuses:
queued,running,awaiting_approval,blocked,succeeded,failed,needs_review,cancelled,timed_out. - Receipt — the evidence record for an attempted action, with outcome
verified,verification_failed,inconclusiveorunverified.
Authentication
Create a workspace API key in the console under Developer. Keys are shown once, stored only as hashes, scoped to permissions you hold, optionally expiring, and revoked automatically if their creator leaves the organization. API keys cannot approve actions.
curl https://api.voidex.net/api/v1/tools \ -H "Authorization: Bearer $VOIDEX_API_KEY"
Submit a task
curl -X POST https://api.voidex.net/api/v1/runs \
-H "Authorization: Bearer $VOIDEX_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: order-8812" \
-d '{
"kind": "action",
"tool": "acme_crm.create_deal",
"connectedAccountId": "acct_…",
"environment": "test",
"input": { "name": "Renewal", "amount": 1200, "currency": "USD" }
}'
# 202 {"runId":"run_…","status":"queued","approvalRequired":false,…}Reusing an idempotency key with the same payload returns the original run; with a different payload it returns 409 idempotency_conflict.
Handle “approval required”
# 202 {"runId":"run_…","status":"awaiting_approval","approvalRequired":true,"approvalRequestId":"apr_…"}
# A person with the approver role decides in the console (or via POST /api/v1/approvals/:id/approve
# with the bindingHash they reviewed). Any payload change invalidates the approval.Status and receipt
curl https://api.voidex.net/api/v1/runs/run_… -H "Authorization: Bearer $VOIDEX_API_KEY"
curl https://api.voidex.net/api/v1/runs/run_…/receipt -H "Authorization: Bearer $VOIDEX_API_KEY"
# {"latest":{"outcome":"verified","checks":[{"type":"record_exists","status":"passed"},…],"hash":"…","prevHash":"…"}}TypeScript SDK
import { VoidexClient } from '@voidex/sdk';
const voidex = new VoidexClient({ baseUrl: 'https://api.voidex.net', apiKey: process.env.VOIDEX_API_KEY! });
const { runId, approvalRequired } = await voidex.submitAction({
tool: 'acme_crm.create_deal',
connectedAccountId: 'acct_…',
input: { name: 'Renewal', amount: 1200, currency: 'USD' },
idempotencyKey: 'order-8812',
});
const run = await voidex.waitForRun(runId);
const { latest } = await voidex.getReceipts(runId);
console.log(latest.outcome, latest.checks);The SDK lives in packages/sdk of the repository. A Python SDK is not available.
Errors
{ "error": { "code": "policy_denied", "message": "…", "correlationId": "cor_…", "retryable": false, "details": {…} } }Webhooks
Events: run.succeeded, run.failed, run.needs_review, run.blocked, approval.requested, approval.resolved, receipt.issued, benchmark.completed. Each delivery carries voidex-signature: t=…,v1=… (HMAC-SHA256 over t.body) and voidex-event-id for deduplication. Reject signatures older than five minutes.
import { verifyWebhook } from '@voidex/sdk';
if (!verifyWebhook(secret, req.headers['voidex-signature'], rawBody)) return res.status(400).end();MCP
Point an MCP client at https://api.voidex.net/api/v1/mcp (streamable HTTP) with your API key as a bearer token. Tools appear as connector__action. MCP is a transport only: policy, approvals and verification still apply, and tool output returned to your agent is data, never instructions to Voidex.