Guides

Agents

Build TypeScript applications that run on a schedule or from a webhook and access Inferada with a dedicated token.

What an agent is

An agent is an instance of a reusable blueprint. Each instance receives a dedicated token with the infa_ prefix, which supports separate usage attribution and token-level limits. Agents call the same model and service endpoints as other API clients.

What you configure

From Organisation → Agents, configure the system prompt, schedule and timezone, token limits, IP allowlist, tools, MCP servers, and blueprint-specific settings. These values can change without rebuilding the agent. An IP allowlist applies to every request made with the agent token, including configuration polls and bundle downloads, so it must include the runtime's egress address.

Managing agents in the portal

Open Organisation → Agents → [agent] to manage a provisioned instance:

  • 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. Neither needs a 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: run the configured flow on demand and inspect step output and screenshots. A debug run uses the real flow but does not create an execution record.

Writing an agent

Build agents with @inferada-ai/agent-kit. It provides the Inferada client, webhook verification, scheduling, configuration polling, and runtime lifecycle so the application can focus on its event handlers.

ts
// src/config.ts — no side effects: the build imports it to read the schema.
import { defineConfigSchema } from '@inferada-ai/agent-kit'
import { z } from 'zod'

// Tune these knobs from the portal; edits apply between runs (no redeploy).
export const configSchema = defineConfigSchema(
  z.object({
    // Model IDs differ per organisation, so the operator picks one.
    model: z.string().describe('Model ID used for the summary')
  })
)

// src/agent.ts
import { defineAgent, run, validateConfig } from '@inferada-ai/agent-kit'
import { configSchema } from './config.js'

run(
  defineAgent({
    // Runs on the schedule you set in the portal.
    async onSchedule({ inferada, config }) {
      const { model } = validateConfig(configSchema, config.config)
      const res = await inferada.chat.completions.create({
        model,
        messages: [{ role: 'system', content: config.system_prompt ?? '' },
                   { role: 'user', content: 'Summarise today’s tickets.' }]
      })
      // ...do something with res
    },
    // Fired by an authenticated inbound webhook (POST to the agent’s public URL).
    async onWebhook(payload, { inferada }) {
      // payload is the JSON you pushed; platform-selected auth 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 fetches it with the agent's own 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 @inferada-ai/agent-kit build --entry src/agent.ts --config src/config.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

In addition to automatic run telemetry, an agent can create its own execution log entries with inferada.executions.create(...). Each entry contains a short status, a human-readable message, and an optional size-limited data object. Entries 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

An agent with an onWebhook handler exposes a public URL. Choose its inbound authentication under Configuration → Webhook authentication. HMAC-SHA256 is recommended: 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 unsigned requests, timestamps more than 5 minutes from its clock, and bodies larger than 1 MiB. After secret rotation, the previous secret remains valid for approximately 24 hours.

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
})

Choose Static Bearer token when the sender cannot compute an HMAC. Send the same webhook secret in the Authorization header. Bearer authentication does not bind the token to the body and captured requests do not expire, so use HTTPS and consider inbound filtering at your firewall, reverse proxy, load balancer, or private network boundary. The agent-token IP allowlist restricts outbound gateway calls; it does not filter inbound webhooks.

ts
await fetch('https://<agent-public-url>/webhook', {
  method: 'POST',
  headers: {
    'content-type': 'application/json',
    'authorization': `Bearer ${WEBHOOK_SECRET}`
  },
  body: JSON.stringify({ ticket_id: 42 })
})

Running on your own premises

Run the same agent on your own infrastructure with its gateway URL and token. It continues to poll configuration, report runs, and use gateway usage attribution and limits.

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