Skip to content

Guides

Outcomes

Let automations write their result back to a record, safely.

Use a record outcome to save an automation's result to the record that triggered it. The write uses that model's access rules and approval policy.

Naming the Record and the Field

The run payload names the record:

{ "record": { "model": "customer", "id": 42 } }

The outcome's target names the field: { action: 'record', target: 'summary' }. The value written is the run's output, as text.

Writing to an Agentic Model

Allow the field in the model with outcomes.writable:

server/models/customers.ts
export default defineAgenticModel(customers, {
  // …
  access: { list: 'member', get: 'member', create: 'admin', update: 'admin', delete: false },
  agent: { tools: ['list', 'get', 'update'], approval: { update: 'always' } },
  outcomes: { writable: ['summary'] },
})

The write follows these model rules:

  • Only fields in outcomes.writable can be written; anything else fails with 403. The list accepts only fields the model can write, so not id, ownerId, workspace, timestamps, hidden, or readonly fields, and it requires an update rule in access.
  • The automation's actor needs the model's update role, and the record must be in scope.
  • The model's approval policy applies: with update: 'always', the change waits in the approvals inbox before it is written.

Writing to Your Own Tables

For another table, provide an updateRecord adapter. The adapter must authorize the row and allow only the fields you choose:

server/agentic.ts
import { and, eq } from 'drizzle-orm'
import { tickets } from './database/tickets'

export default defineNuxtAgentic({
  async updateRecord({ model, id, field, value }, { database, actor }) {
    if (model !== 'ticket' || field !== 'resolution') {
      throw new Error('Only ticket resolutions can be updated.')
    }
    await database
      .update(tickets)
      .set({ resolution: value })
      .where(and(eq(tickets.id, Number(id)), eq(tickets.workspace, actor.workspace)))
  },
})

In Void, pass the same function as updateRecord to defineVoidAgentic.

The change also carries runId, runAs, and an idempotencyKey unique to the run, so an adapter can ignore a retried write. The second argument has database, actor, env, and signal.

Before your adapter runs, some targets are always refused:

  • Agentic and auth records: agent, automation, automationRun, automationFolder, connection, integrationConnection, integrationApproval, approval, organization, member, user, session, account, verification, oauthState, and any model starting with agentic_.
  • The fields id, owner, workspace, permissions, and role, and fields ending in Id or _id.

Without an updateRecord adapter, a record outcome for a model that is not registered fails.