Guides

Agents

Run small TypeScript apps that complete jobs/tasks on a schedule or via webhook, calling your organisation’s LLM services with their own token — fully audited and limited.

What an agent is

An agent is a small TypeScript app — staged in code by Inferada as a reusable blueprint — that your organisation instantiates and configures. Each agent gets its own token (prefix infa_), not a user’s, so its usage is independently auditable and can be capped with per-token limits. Agents call the same gateway service endpoints your API tokens use (chat/completions, pii, language, tts/stt), and can pick a specific configured service per call.

What you configure

From Organisation → Agents you set the live, no-redeploy settings: the main system prompt, the run schedule (cron + timezone), per-token limits and IP allowlist, enabled tools / MCP servers, and any blueprint-specific knobs. Staging the agent code and provisioning the runtime is handled by Inferada, so there is nothing to install or deploy yourself. Careful with the IP allowlist: it gates every call the agent’s token makes, including the agent’s own config polls and bundle fetches — the list must include the agent host’s egress IP, or the agent takes itself offline on its next poll.

Managing agents in the portal

Inferada sets each agent up for your organisation from a ready blueprint and brings its runtime online. From Organisation → Agents → [agent] you then run and tune it:

  • Configure — change any of the settings above. Edits are live: Save applies them on the agent’s next run, or Apply config now pushes them immediately — no redeploy (that’s only for new agent code).
  • Rotate secrets — roll the agent’s token or its webhook secret whenever you need to. Each is shown once, so copy it then.
  • Review activity — browse run history and the agent’s execution log to see what it has been doing.
  • Debug tab — trigger the agent’s configured flow on demand and watch it live: per-step console output and a screenshot after each step, streamed straight from the running agent. Handy for confirming a config change behaves before you rely on it; it drives the real flow but doesn’t record an execution.

Writing an agent

Agents are built against @inferada/agent-kit, which gives you a per-scope Inferada SDK, an HMAC-verified webhook server, a local scheduler, a config poller, and a drain-aware lifecycle — so you write handlers, not plumbing.

ts
import { defineAgent, run, defineConfigSchema } from '@inferada/agent-kit'
import { z } from 'zod'

// Tune these knobs from the portal; the gateway validates edits
// against this schema and the agent reads them live (no redeploy).
export const configSchema = defineConfigSchema(
  z.object({ greeting: z.string().default('Hello') })
)

export default run(
  defineAgent({
    // Runs on the schedule you set in the portal.
    async onSchedule({ inferada, config }) {
      const res = await inferada.chat.completions.create({
        model: 'qwen3.5-35b',
        messages: [{ role: 'system', content: config.system_prompt ?? '' },
                   { role: 'user', content: 'Summarise today’s tickets.' }]
      })
      // ...do something with res
    },
    // Fired by a signed inbound webhook (POST to the agent’s public URL).
    async onWebhook(payload, { inferada }) {
      // payload is the JSON you pushed; HMAC already verified.
    }
  })
)

Build and deploy

The kit’s build command compiles your agent into a self-contained zip with a manifest (entrypoint + your config schema). Inferada uploads it to the blueprint as a versioned bundle; the managed runtime container fetches it over the agent token, verifies the checksum, extracts it, and runs it. Config and schedule changes apply on the next poll; a new bundle triggers a clean restart.

bash
# Build a self-contained bundle (compiled app + manifest.json) ...
npx agent-kit build --entry src/agent.ts --out dist/agent.zip

# ... then it's handed to Inferada, which stages it as the agent's bundle.
# The runtime fetches + verifies (checksum) + runs it on boot.

Recording execution logs

Beyond the automatic per-run telemetry, an agent can record its own freeform execution log entries with inferada.executions.create(...) — a short status, a human-readable message, and an optional structured data blob (kept small; the gateway caps its size). Write as many as you like per run to leave a trail of what the agent did. They appear under Organisation → Agents → (agent) → Execution log, newest first, with the data expandable.

ts
export default run(
  defineAgent({
    async onSchedule({ inferada }) {
      const items = await loadWork()
      // Record a freeform log line you can browse later.
      await inferada.executions.create({
        status: 'success',
        message: `Processed ${items.length} items`,
        data: { ids: items.map((i) => i.id) }
      })
    }
  })
)

Triggering by webhook

If an agent defines an onWebhook handler it exposes a public URL. Sign `${t}.${body}` — a unix-seconds timestamp, a dot, then the raw JSON body — with the agent’s webhook secret (HMAC-SHA256) and send both in the X-Inferada-Signature header as t=<ts>,v1=<hex>. The agent rejects anything unsigned, any signature stamped more than 5 minutes from its clock — replaying a captured request past that window fails — and bodies over 1 MiB. After you rotate the secret, the previous one keeps verifying for a ~24 hour grace window, then expires automatically.

ts
import crypto from 'node:crypto'

const body = JSON.stringify({ ticket_id: 42 })
const t = Math.floor(Date.now() / 1000)
const v1 = crypto.createHmac('sha256', WEBHOOK_SECRET).update(`${t}.${body}`).digest('hex')

await fetch('https://<agent-public-url>/webhook', {
  method: 'POST',
  headers: { 'content-type': 'application/json', 'x-inferada-signature': `t=${t},v1=${v1}` },
  body
})

Running on your own premises

The same agent runs anywhere with just the gateway URL and an agent token — in Inferada’s managed runtime or on your own infrastructure. Either way it polls its config, reports its runs, and is audited + limited through the gateway’s public API identically.

bash
docker run --rm \
  -e INFERADA_API_URL=https://api.inferada.example/v1 \
  -e INFERADA_API_TOKEN=infa_… \
  your-registry/your-agent:latest