RobotRock

Send to human

Use robotrock.sendToHuman() from your shared client module to send an approval request. Set ROBOTROCK_API_KEY in your environment (named keys in Settings → API Keys).

Client setup

// lib/robotrock.ts
import { createClient } from "robotrock";

export const robotrock = createClient({
  app: "my-service",
  version: process.env.AGENT_VERSION,
  webhook: {
    url: "https://your-app.com/api/robotrock/webhook",
  },
});

JSON Forms foundation

RobotRock task forms are built on top of JSON Forms concepts:

  • JSON Schema defines the structure and validation of response data.
  • UI schema controls field rendering (widgets, labels, placeholders, options).

You provide these via action schema and ui fields.

Minimal example

import { robotrock } from "@/lib/robotrock";

const response = await robotrock.sendToHuman({
  type: "budget-approval",
  name: "Approve Q4 budget update",
  actions: [
    { id: "approve", title: "Approve" },
    { id: "reject", title: "Reject" },
  ],
});

Top-level task fields

import { robotrock } from "@/lib/robotrock";

await robotrock.sendToHuman({
  type: "string (required)",
  name: "string (required)",
  description: "string (optional)",
  validUntil: new Date(Date.now() + 2 * 60 * 60 * 1000), // Date or ISO string (optional)
  context: {
    data: {},
    ui: {},
  }, // optional
  actions: [], // required, at least one action
  idempotencyKey: "optional-retry-safe-key",
  threadId: "optional-thread-id", // group related tasks; omit to start a new thread
  priority: "urgent", // optional — thread-scoped: low | normal | high | urgent
  update: { message: "Build finished.", status: "waiting" }, // optional — log an initial thread update
  assignTo: {
    users: ["alice@acme.com"],
    groups: ["finance"],
  }, // optional — narrows inbox visibility
  version: "1.3.1", // optional — per-task agent version override
});

app and agent version are configured on createClient (defaults to AGENT_VERSION from env). Override per task with top-level version on sendToHuman.

The server returns a threadId on response.task.threadId. Reuse it on later tasks to group them in the inbox—see Threads.

Priority (priority)

Optional top-level field to set how important a thread is in the inbox. Values: low, normal, high, urgent. When set on a task, it applies to the whole thread and overwrites any previous priority. Omit on later tasks to leave the thread unchanged. Defaults to normal.

See Priority for semantics, sorting, and examples.

Assignment (assignTo)

Optional top-level field (not stored inside task context JSON):

assignTo?: {
  users?: string[];  // tenant member emails
  groups?: string[]; // group slugs, e.g. "finance"
};

Visibility:

assignTo.groupsVisible to
omitted / ["all"]All workspace members
["admins"]Tenant admins only
["finance"] (custom slug)Members of that group
  • Omitted — task is assigned to the tenant All group; every member sees it.
  • Set — only listed users and members of listed groups see the task in the inbox.
  • { groups: ["all"] } — same as omitting assignTo.
  • { groups: ["admins"] } — only workspace administrators see the task (virtual group synced from admin role).

Invalid emails or unknown group slugs return 400. You cannot combine "all" with other group slugs. "admins" may be combined with other groups and users.

Agent version

Set the agent release on createClient so Statistics and feedback analysis can compare human feedback over deploys:

export const robotrock = createClient({
  app: "my-agent",
  version: process.env.AGENT_VERSION,
});

Wire format (set automatically by the SDK):

agent?: { version: string }; // semver, git SHA, or deploy tag
await robotrock.sendToHuman({
  type: "budget-approval",
  name: "Approve Q4 spend",
  actions: [
    { id: "approve", title: "Approve" },
    { id: "reject", title: "Reject" },
  ],
  version: "1.3.1", // optional override of client version
});

Agent improvement loop

  1. Set version on createClient (or pass per task)
  2. Humans handle tasks (action + feedback forms)
  3. Run feedback analysis from Statistics (or wait for the weekly cron)
  4. Before changing agent code, call MCP get_feedback_analysis with the same app and type
  5. Apply agentInstructions when isHealthy is false

See MCP integration for get_feedback_analysis.

Trigger.dev and Vercel Workflow (handled → OTel)

On durable platform tasks, enable trace recording so human decisions appear on the run trace (not stored in RobotRock):

export ROBOTROCK_OTEL_RECORD_HANDLED=true
await sendToHumanTask.triggerAndWait({
  type: "deploy-approval",
  name: "Approve deploy",
  actions: [{ id: "approve", title: "Approve" }, { id: "reject", title: "Reject" }],
  recordOtel: true,
});

The SDK adds span robotrock.wait_for_human, event robotrock.task_handled, and attributes such as robotrock.action.id and robotrock.human_wait_ms.

Inbox routing

Set app on createClient to group tasks in the dashboard inbox.

When app is omitted on the client, the API uses your API key name as the inbox bucket.

Webhooks

Configure webhook: { url, headers? } on createClient. The webhook applies to every action. See Webhooks.

Without a webhook, sendToHuman() polls until handled. Set polling on createClient (not on each task). Polling respects each task's validUntil deadline—see Polling.

Platform terminal actions

Reviewers can mark as done or reject the request from the inbox without choosing your task actions. When handled, action.id is a reserved platform id — stop your agent and do not retry:

  • robotrock:mark-done — closed manually (data: {})
  • robotrock:reject-request — bad agent output (data: { feedback: string })
import { shouldStopAgentForHandledAction, parseHandledOutcome } from "robotrock";

if (result.mode === "handled" && shouldStopAgentForHandledAction(result.actionId)) {
  const outcome = parseHandledOutcome({
    actionId: result.actionId,
    data: result.data,
  });
  return; // terminal — do not continue
}

Exported constants: PLATFORM_MARK_DONE_ACTION_ID, PLATFORM_REJECT_REQUEST_ACTION_ID. See Webhooks — Platform action IDs.

Split references

  • For all context fields and widgets, see Context.
  • For complete task payload examples, see Examples under Tasks in the sidebar.
  • For all actions fields and examples, see Actions.
  • For grouping related tasks together, see Threads.
  • For thread priority and inbox ordering, see Priority.
  • For sending status updates to a thread, see Updates.
  • For getTask, cancelTask, and task statuses, see Task lifecycle.

On this page