Skip to main content
T1 starter · every office job — the first example with infer.schema: — the model doesn’t get to answer in prose. It returns the JSON shape you declared, or the engine rejects it.

The job

The meeting ended an hour ago and nobody remembers who owns what. The transcript is sitting in a file. This workflow extracts every action item as {owner, task, due} — typed, validated, importable by whatever tracker you use.

The shape

The file

t1-meeting-actions.nika.yaml
nika: v1
workflow: meeting-actions
description: "Transcript → typed action items {owner, task, due}"

model: mock/echo            # swap for openai/gpt-5.2 or any provider in the catalog

vars:
  transcript_path:
    type: string
    required: true
    description: "Path to the raw meeting transcript"

tasks:
  - id: transcript
    invoke:
      tool: "nika:read"
      args: { path: "${{ vars.transcript_path }}" }

  - id: extract
    depends_on: [transcript]
    infer:
      prompt: |
        Extract every action item from this meeting transcript ·
        ${{ tasks.transcript.output }}
      schema:                          # the contract the model must satisfy
        type: object
        required: [actions]
        properties:
          actions:
            type: array
            items:
              type: object
              required: [owner, task]
              properties:
                owner: { type: string }
                task: { type: string }
                due: { type: string }

  - id: save
    depends_on: [extract]
    invoke:
      tool: "nika:write"
      args:
        path: "./action-items.json"
        content: "${{ tasks.extract.output.actions }}"

  - id: trace
    depends_on: [extract]
    invoke:
      tool: "nika:log"
      args:
        level: info
        message: "Action items extracted to ./action-items.json"

outputs:
  actions:
    value: ${{ tasks.extract.output.actions }}
    type: array
    description: "Typed action items, tracker-ready"

How it works

1

A required, typed input

transcript_path is declared with type: string · required: true — the engine validates inputs before any task runs, and the workflow becomes callable with a real contract.
2

The schema IS the contract

infer.schema: is standard JSON Schema. The model must return a matching object — on violation the engine rejects (and may retry). Downstream tasks index into it directly: ${{ tasks.extract.output.actions }}.
3

Two consumers, no extra ceremony

save writes the JSON, trace logs a human-readable line — both depend on extract, both run in parallel.

Constructs you just used

ConstructWhereReference
typed vars:transcript_pathYAML syntax
infer.schema:extractThe 4 verbs
schema-path indexingsave.args.contentBindings
nika:logtraceBuiltins

Make it yours

  • Add due to the required: list if your team always sets deadlines.
  • Chain a nika:notify task that posts each owner their items.
  • Swap the transcript file for a nika:fetch of your meeting tool’s export API — the schema doesn’t change.

Next · Price watch

The next starter has zero model calls — pure tools, bindings and one CEL comparison.