genroc

docs / Guides / Process definition

External tasks

Task resolved from outside

Processes can expose a task to be solved by human or machine.

External task works in a similar way as a child task, it can have specified input and result_schema. It parks the process until it a result is submited.

Example

# approval.genroc.yaml
nameUnique process identifier.: approvalUnique process identifier.
input_schemaJSON Schema used to validate the input payload when starting a new instance.:
  typeThe value's JSON type, or a list of types it may take.: objectThe value's JSON type, or a list of types it may take.
  propertiesThe named members of an object, each a schema.:
    amount: {typeThe value's JSON type, or a list of types it may take.: integerThe value's JSON type, or a list of types it may take.}
  requiredWhich properties must be present. A property not listed here may be absent, and reading it yields null.: [amount]

tasksOrdered list of execution tasks. Control advances linearly unless a switch case redirects.:
  - idTask identifier, unique within the definition.: reviewTask identifier, unique within the definition.
    actionDescribes the action to perform. Omit for switch-only (routing) tasks.:
      typeExternal task: parks the instance until an outside caller submits a result. No worker is held.: externalExternal task: parks the instance until an outside caller submits a result. No worker is held.
      inputtasks.review.action.input — object{amount}:
        amountamount — integer: "$: inputinput → object{amount}.amountinput.amount → integer"
      result_schemaJSON Schema the submitted result is validated against. Without it any JSON is accepted.:
        typeThe value's JSON type, or a list of types it may take.: objectThe value's JSON type, or a list of types it may take.
        propertiesThe named members of an object, each a schema.:
          approved: {typeThe value's JSON type, or a list of types it may take.: booleanThe value's JSON type, or a list of types it may take.}
        requiredWhich properties must be present. A property not listed here may be absent, and reading it yields null.: [approved]
    outputself.result.approved → boolean: "$: selfself → object{result}.resultself.result → object{approved}.approvedself.result.approved → boolean"
    switchRequired. Routing: a shorthand ("next", "end", "$task-id") or an ordered list of cases.: endRequired. Routing: a shorthand ("next", "end", "$task-id") or an ordered list of cases.

outputoutput — object{approved}:
  approvedapproved — boolean: "$: outputsoutputs → object{review}.reviewoutputs.review → boolean"

Let’s apply the example process and run it.

> genctl apply                       
latest: approval v1 (current)

> genctl run approval --set amount=10
started: 5fy765kh  approval@v1  (running)

If we get the state of the process after it started, we’ll see it has Phase: external.

> genctl get @last                   
ID:       5fy765kh
Process:  approval@v1
Status:   running
Task:     review
Phase:    external

We can resolve the task through cli with signal command.

genctl signal @last --task review --set approved=true
signaled: 5fy765kh  task=review  (delivered)

Once resolved, the process continues it’s execution to completion.

> genctl get 5fy765kh                                      
ID:       5fy765kh
Process:  approval@v1
Status:   completed
Task:     review
Created:  2026-09-24 14:23:27
Updated:  2026-09-24 14:44:51

Output:
approved: true

Listing processes waiting for resolution

You can filter process which are waiting for external resolution with the regular instances command.

> genctl instances --phase external
ID        STATUS            PROCESS      UPDATED  CREATED  CODE  ERROR
3kjfx4vm  running·external  approval@v1  1m ago   1m ago         
02pcayvt  running·external  approval@v1  1m ago   1m ago         
4cqpdx8z  running·external  approval@v1  1m ago   1m ago         
8nte1end  running·external  approval@v1  1m ago   1m ago

Signals

In the example above we’ve used genctl signal command to submit the result of external task. Signals can be submitted any time (even if the process is not waiting for it). Genroc puts submitted signals into a queue and it is picked up automatically when needed.

Queues

If we want to resolve external tasks in a systematic way, we need to have a queue and workers resolving the tasks.

We could use instances filter and pick the process we want to resolve, but that is problematic when we have multiple workers - they need to know which process is already taken.

Genroc offers a REST API for this.

Claiming

You can use /claim endpoint wich will ensure no other worker can work on it at the same time. Claimed tasks are leased to the worker for the lease_ms time.

Lease renewal

Your worker should regularly prolong the leases (unless you can guarantee it will always finish in certain time). Lease renewals can be understood as a sort of heartbeat letting genroc know that the worker is alive.

The /renew endpoint will also let you know if the process is cancelled and the result is no longer needed. You can then kill the worker (usefull for really long tasks).

Resolving task

After the work is finished, use /resolve endpoint with submitted token from the claim. Resolve endpoint is different from signal, because it can only be used with previously claimed task and it requires the process to be waiting for the result.

Releasing the lease

Hand a claim back to the queue immediately wit /release instead of waiting out its lease.