Skip to workflow content
Vora
← Workflow library

Conversation Operations

Turn reviewed call notes into an owned handoff

Turn already reviewed structured call notes into an actionable handoff. Preserve the approved summary and action text while making unowned or undated work visible.

Experimental native example · local preview · revision 1

WORKFLOW PREVIEW

See how it works

2 nodes · 1 connections

Use + and − to zoom, F to fit. Press Enter on a node to inspect it.

Private preview. These tested drafts are available for inspection. Adding them to a Vora workspace is still being tested.

What you need

  • A reviewed summary and structured action list authorized for the workspace.
  • Unique action IDs, supplied owner IDs and due dates, or empty owner/date values that must remain flagged.
Follow the workflow step by step
  1. Supplied agency data

    Receive the explicitly supplied manual input packet.

  2. Turn reviewed call notes into an owned handoff

    Return a normalized summary and ordered action packet, flagging unowned or undated actions for review. Consume already reviewed structured notes; do not claim transcript extraction or task creation.

Setup and review

  • Supply the call ID, reviewed summary and at most 100 actions.
  • Test a complete action, an unowned action and a call with no actions.
  • Inspect each reviewReasons list before a separate task-creation or CRM action is connected.

Inspect every node

Expand a node to inspect its complete settings. Review the runtime and adoption evidence before installing it.

Supplied agency data · wfManualTrigger
{
  "label": "Supplied agency data"
}
Turn reviewed call notes into an owned handoff · code
{
  "label": "Turn reviewed call notes into an owned handoff",
  "code": "\nfunction obj(value, keys, required = keys) {\n  if (!value || typeof value !== 'object' || Array.isArray(value)) throw new Error('Expected object');\n  if (Object.keys(value).some(key => !keys.includes(key))) throw new Error('Unknown input field');\n  if (required.some(key => !Object.hasOwn(value, key))) throw new Error('Missing required input field');\n  return value;\n}\nfunction text(value, name, max = 200, allowEmpty = false) {\n  if (typeof value !== 'string' || value.length > max || (!allowEmpty && !value.trim())) throw new Error('Invalid ' + name);\n  return value.trim();\n}\nfunction list(value, name, max = 500) {\n  if (!Array.isArray(value) || value.length > max) throw new Error('Invalid ' + name);\n  return value;\n}\nfunction int(value, name, min = 0, max = Number.MAX_SAFE_INTEGER) {\n  if (!Number.isSafeInteger(value) || value < min || value > max) throw new Error('Invalid ' + name);\n  return value;\n}\nfunction amount(value, name) {\n  if (!Number.isFinite(value) || value < 0 || value > 1000000000) throw new Error('Invalid ' + name);\n  return value;\n}\nfunction email(value) { return /^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$/.test(value); }\nfunction day(value) {\n  if (typeof value !== 'string' || !/^\\d{4}-\\d{2}-\\d{2}$/.test(value)) throw new Error('Invalid calendar date');\n  const ms = Date.parse(value + 'T00:00:00Z');\n  if (!Number.isFinite(ms) || new Date(ms).toISOString().slice(0, 10) !== value) throw new Error('Invalid calendar date');\n  return ms;\n}\nfunction instant(value) {\n  if (typeof value !== 'string' || !/^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}(\\.\\d{3})?Z$/.test(value)) throw new Error('Invalid UTC timestamp');\n  const ms = Date.parse(value);\n  const normalized = value.includes('.') ? value : value.replace('Z', '.000Z');\n  if (!Number.isFinite(ms) || new Date(ms).toISOString() !== normalized) throw new Error('Invalid UTC timestamp');\n  return ms;\n}\nfunction uniqueIds(items) {\n  const ids = items.map(item => text(item.id, 'item ID', 120));\n  if (new Set(ids).size !== ids.length) throw new Error('Duplicate item ID');\n  return ids;\n}\nfunction ratioBps(numerator, denominator) {\n  const n = BigInt(numerator) * 10000n;\n  const d = BigInt(denominator);\n  const quotient = n / d;\n  const floor = n < 0n && n % d !== 0n ? quotient - 1n : quotient;\n  const result = Number(floor);\n  if (!Number.isSafeInteger(result)) throw new Error('Unsafe calculated ratio');\n  return result;\n}\n\n\nconst x = obj(context.input, ['callId','summary','actions']);\nconst callId = text(x.callId, 'call ID', 120);\nconst summary = text(x.summary, 'summary', 4000).replace(/\\s+/g, ' ');\nconst items = list(x.actions, 'actions', 100);\nitems.forEach(item => obj(item, ['id','text','ownerId','dueDate']));\nconst ids = uniqueIds(items);\nconst actions = items.map((item,index) => {\n  const actionText = text(item.text, 'action text', 1000).replace(/\\s+/g, ' ');\n  const ownerId = text(item.ownerId, 'owner ID', 120, true), dueDate = text(item.dueDate, 'due date', 10, true);\n  if (dueDate) day(dueDate);\n  const reviewReasons = [];\n  if (!ownerId) reviewReasons.push('missing_owner');\n  if (!dueDate) reviewReasons.push('missing_due_date');\n  return {id:ids[index], text:actionText, ownerId, dueDate, reviewReasons};\n});\nconst status = !actions.length ? 'no_actions' : actions.some(item => item.reviewReasons.length) ? 'review' : 'ready';\nreturn {callId, summary, actions, status};\n",
  "outputVar": "result",
  "timeout": 1000,
  "memoryLimit": 16
}
Input and output contracts

Input

{
  "type": "object",
  "properties": {
    "callId": {
      "type": "string",
      "maxLength": 120
    },
    "summary": {
      "type": "string",
      "maxLength": 4000
    },
    "actions": {
      "type": "array",
      "items": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "maxLength": 120
          },
          "text": {
            "type": "string",
            "maxLength": 1000
          },
          "ownerId": {
            "type": "string",
            "maxLength": 120
          },
          "dueDate": {
            "type": "string",
            "maxLength": 10
          }
        },
        "required": [
          "id",
          "text",
          "ownerId",
          "dueDate"
        ],
        "additionalProperties": false
      },
      "maxItems": 100
    }
  },
  "required": [
    "callId",
    "summary",
    "actions"
  ],
  "additionalProperties": false
}

Output

{
  "type": "object",
  "properties": {
    "callId": {
      "type": "string",
      "maxLength": 120
    },
    "summary": {
      "type": "string",
      "maxLength": 4000
    },
    "actions": {
      "type": "array",
      "items": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "maxLength": 120
          },
          "text": {
            "type": "string",
            "maxLength": 1000
          },
          "ownerId": {
            "type": "string",
            "maxLength": 120
          },
          "dueDate": {
            "type": "string",
            "maxLength": 10
          },
          "reviewReasons": {
            "type": "array",
            "items": {
              "type": "string",
              "maxLength": 200
            },
            "maxItems": 2
          }
        },
        "required": [
          "id",
          "text",
          "ownerId",
          "dueDate",
          "reviewReasons"
        ],
        "additionalProperties": false
      },
      "maxItems": 100
    },
    "status": {
      "type": "string",
      "maxLength": 20
    }
  },
  "required": [
    "callId",
    "summary",
    "actions",
    "status"
  ],
  "additionalProperties": false
}

Choose an output for this job

This business workflow also has 15 delivery formats. Compare their inputs, output examples and limits.

Compare implementations for this job

Example input and result

These examples use synthetic data.

Input

{
  "callId": "call-01",
  "summary": " Buyer requested a pilot. ",
  "actions": [
    {
      "id": "a1",
      "text": "Send the approved scope",
      "ownerId": "owner-01",
      "dueDate": "2026-10-09"
    }
  ]
}

Expected result

{
  "callId": "call-01",
  "summary": "Buyer requested a pilot.",
  "actions": [
    {
      "id": "a1",
      "text": "Send the approved scope",
      "ownerId": "owner-01",
      "dueDate": "2026-10-09",
      "reviewReasons": []
    }
  ],
  "status": "ready"
}

Success means: Return a normalized summary and ordered action packet, flagging unowned or undated actions for review. Consume already reviewed structured notes; do not claim transcript extraction or task creation.

When something goes wrong

  • Blank summaries, duplicate action IDs and invalid nonempty due dates stop the packet. Correct the reviewed notes. Missing owner or date produces review, preserving the action instead of inventing an assignment.

Limits and costs

  • The workflow normalizes whitespace in supplied text. It does not transcribe a recording, use an LLM to extract facts, create tasks, identify speakers or deliver a follow-up message.
  • No paid model calls or metered third-party operations are declared in this pure graph. Vora plan availability, compute and quotas still apply; pricing has not been verified by this batch.

USE IN VORA

Turn reviewed call notes into an owned handoff

Review this workflow’s steps and required inputs. Get access to Vora before setting it up in your workspace.

Workspace installation is being connected. This page lets you inspect the workflow; it does not create or run one.

Required input fields
{
  "additionalProperties": false,
  "properties": {
    "actions": {
      "items": {
        "additionalProperties": false,
        "properties": {
          "dueDate": {
            "maxLength": 10,
            "type": "string"
          },
          "id": {
            "maxLength": 120,
            "type": "string"
          },
          "ownerId": {
            "maxLength": 120,
            "type": "string"
          },
          "text": {
            "maxLength": 1000,
            "type": "string"
          }
        },
        "required": [
          "id",
          "text",
          "ownerId",
          "dueDate"
        ],
        "type": "object"
      },
      "maxItems": 100,
      "type": "array"
    },
    "callId": {
      "maxLength": 120,
      "type": "string"
    },
    "summary": {
      "maxLength": 4000,
      "type": "string"
    }
  },
  "required": [
    "callId",
    "summary",
    "actions"
  ],
  "type": "object"
}