SDKs

Contro1 JavaScript and TypeScript SDK

Install and use @contro1/sdk in Node, TypeScript, Express, Fastify, or Next.js backends to create approval requests, verify webhooks, and write audit records.

Use @contro1/sdk when your agent backend, tool runner, webhook bridge, or application server runs on Node or TypeScript.

Use the integration skill

Copy this skill link into your code agent to add JavaScript / TypeScript SDK and Contro1 to your system.

Copy skill link

Key takeaways

  • Install the SDK with npm install @contro1/sdk.
  • Use createProtocolRequest for production approval gates.
  • Use webhookMiddleware or verifyWebhook before resuming actions.
  • Use logAction for audit-only records and post-approval outcomes.

Install

Use the JavaScript/TypeScript SDK for Node services, Express or Fastify webhook bridges, Next.js API routes, and custom TypeScript agent runtimes.

terminal
npm install @contro1/sdk
.env
CENTCOM_API_KEY=cc_live_your_key
CENTCOM_BASE_URL=https://api.contro1.com/api/centcom/v1
CENTCOM_WEBHOOK_SECRET=whsec_your_secret

Initialize the client

contro1Client.ts
import { CentcomClient } from '@contro1/sdk';

export const centcom = new CentcomClient({
  apiKey: process.env.CENTCOM_API_KEY!,
  baseUrl: process.env.CENTCOM_BASE_URL,
});

Create an approval request

Create the request before the risky tool or workflow step executes. Use external_request_id for retries and correlation_id to group related requests and audit records.

Build context at the gate: copy the exact tool input your code intercepted (machine-observed), attach what triggered the run, and carry the agent's justification by making reason a required parameter of the risky tool. Label model-written text as agent-reported - it informs the reviewer but never changes routing or risk_level.

approvalRequest.ts
// "reason" is a required tool parameter: the model justifies at decision time
async function deploy(service: string, version: string, reason: string) {
  const request = await centcom.createProtocolRequest({
    title: 'Approve production deploy?',
    request_type: 'approval',
    source: { integration: 'deploy-agent', workflow_id: 'release', run_id },
    routing: { required_role: 'release_manager', priority: 'urgent', sla_minutes: 10 },
    context: {
      action_type: 'deploy',
      resource: service,
      tool_input: { service, version },
      summary: 'Deploy release with billing migration to production.',
      agent_reported: { justification: reason },
    },
    risk_level: 'high',
    policy_trigger: 'Production deploys with billing migrations require release approval.',
    approval_comment_required: true,
    continuation: { mode: 'decision', webhook_url: 'https://app.example.com/api/contro1/webhook' },
    external_request_id: `deploy:${run_id}:billing-api`,
    correlation_id: `case_deploy_${run_id}`,
  });
  return request;
}

Verify signed callbacks

expressWebhook.ts
import { webhookMiddleware } from '@contro1/sdk';

app.post(
  '/api/contro1/webhook',
  webhookMiddleware(process.env.CENTCOM_WEBHOOK_SECRET!),
  async (req, res) => {
    const payload = req.body;
    if (payload.status !== 'approved') return res.status(200).send('denied');
    await resumeAction(payload.response);
    res.status(200).send('ok');
  },
);
verifyWebhook.ts
import { verifyWebhook } from '@contro1/sdk';

const valid = verifyWebhook(rawBody, signature, timestamp, process.env.CENTCOM_WEBHOOK_SECRET!);
if (!valid) throw new Error('Invalid Contro1 webhook signature');

Log autonomous actions

auditRecord.ts
await centcom.logAction({
  action: 'deploy.healthcheck_passed',
  summary: 'Post-deploy health check passed for billing-api.',
  source: { integration: 'deploy-agent', workflow_id: 'release', run_id },
  outcome: 'success',
  severity: 'info',
  correlation_id: caseId,
  in_reply_to: { type: 'request', id: request.id },
});

Fetch evidence

requestEvidence.ts
const evidence = await centcom.get(`/requests/${requestId}/evidence`);
caseTimeline.ts
const timeline = await centcom.get(`/cases/${caseId}`);

Send full agent traceability

Beyond the approval call, attach identity, a run trace, the tools you invoked, and the context you retrieved. Each field is optional — add what you have. The verified identity always comes from your API key; a caller-supplied actor.agent_id is recorded as a claimed sub-agent until an admin verifies it.

  • trace_id / parent_trace_id — link one run (and sub-agent runs) into a single trace.
  • tool_calls[] — what the agent tried to do, so reviewers see the actions.
  • retrieved_context[] — the data the decision was based on (RAG provenance).
  • Then export a signed evidence packet from GET /requests/:id/evidence.
Send full traceability
// Same approval call you already make — now with full traceability.
await client.requests.create({
  request_type: "approval",
  title: "Refund $4,200 to customer 8831",
  source: { integration: "api" },
  actor: { agent_id: "billing-agent", agent_name: "Billing Agent" }, // claimed sub-agent
  trace_id: `trc_${runId}`,          // link every step of this run
  tool_calls: [{ name: "lookup_order", outcome: "success" }],
  retrieved_context: [{ source: "policy:refunds", uri: "kb://policy/refunds" }],
  continuation: { mode: "decision" },
});

Agent identity, traceability & signed evidence

Make the approval the gate, not a suggestion

The signed webhook is cryptographic proof of a human decision. Verify it inside the system that executes the action - not inside the agent. Any tool that must never run without human sign-off (payments, deploys, data deletion) should refuse to act without a verified approval; that way no agent, including shadow agents nobody registered, can trigger it by skipping Contro1.

  • Verify the signature and reject timestamps older than 5 minutes (replay protection; the timestamp marks callback delivery, not request creation, so long SLAs are unaffected).
  • Bind the approval to the exact action parameters via metadata / correlation_id - never treat "an approval arrived" as permission for a different action.
  • Execute each request_id exactly once (idempotency on your side).
  • When in doubt, confirm state directly with GET /v1/requests/:id using a read-only API key.

Full guardrail pattern with code: Webhooks

Frequently asked questions

Is this a Java SDK?

No. The current public repo is JavaScript/TypeScript for Node runtimes. A Java SDK would need a separate package and repo.

Should I use webhooks or polling?

Use webhooks in production when possible. Polling is useful for local tools, jobs without inbound HTTP, or early prototypes.

Related resources

Contro1 Python SDK

Install and use the framework-agnostic Contro1 Python SDK to request human approvals or input, verify signed webhooks, log autonomous actions, and fetch audit evidence.