# Child jobs

> Delegate durable work from a handler and join its result without holding a worker slot.

`HandlerContext.runChild` creates a child and moves its parent to `blocked` in one transaction. The
parent releases its lease while the child runs, then restarts from the top when the child settles.

<Tabs items={["TypeScript", "Python", "Go"]}>
  <Tab value="TypeScript">
    ```ts
    const charge = await context.runChild<{ orderId: string }, { receiptId: string }>(
      "charge",
      "payments.charge",
      { orderId: order.id },
      { queue: "payments" },
    );
    ```
  </Tab>
  <Tab value="Python">
    ```python
    charge = context.run_child(
        "charge",
        "payments.charge",
        {"orderId": order["id"]},
        EnqueueOptions(queue="payments"),
    )
    ```
  </Tab>
  <Tab value="Go">
    ```go
    charge, err := handler.CreateChild(
        "charge",
        "payments.charge",
        map[string]any{"orderId": order.ID},
        workhorse.EnqueueOptions{Queue: "payments"},
    )
    ```
  </Tab>
</Tabs>

On replay, the stable child name and request return the retained result instead of creating another
job. Changing the name, payload, type, options, or set membership raises `ChildConflictError`, so a
deploy cannot silently rewrite an in-flight parent.

Use `runChildren` to create and join independent work in parallel. PostgreSQL releases the parent
only after the whole set settles. The method returns a tagged `succeeded`, `failed`, or `canceled`
outcome for every child, so the parent handler chooses its own final result.

Python handlers call `run_child` or `run_children`. Go handlers call `CreateChild` or
`CreateChildren`. Both set methods retain the declared names and request order. Use
`runChildrenAll`, `run_children_all`, or `CreateChildrenAll` when a child failure or cancellation
must propagate to the parent.

Go represents the three tags as `ChildSucceeded`, `ChildFailed`, and `ChildCanceled`.

Code before either call runs again when the parent resumes. Wrap external effects in checkpoints or
make them independently idempotent.

## Inspect the tree

`Admin.getJob` and `Admin.listJobs` expose `parentJobId` and `childJobIds`.
`Admin.getChildLineage(jobId)` reads retained edges in either direction, and the dashboard links
parents and children with their names and join state.

TypeScript handlers create children with `HandlerContext.runChildren`, which returns each
`ChildOutcome`. Use `HandlerContext.checkpoint` around replay-sensitive surrounding work. Workhorse
raises `ChildLimitExceededError` or `ChildResultLimitExceededError` before a tree or retained result
can grow without a bound.

## Next

- [Job dependencies](/docs/job-dependencies) — block work declared by a producer
- [Agentic flow](/docs/agentic-flow) — compose child tools with other durable boundaries
- [Cancellation](/docs/cancellation) — stop a waiting parent or active child

---

Exact child schema and lifecycle semantics:
[architecture reference](https://github.com/stablemates/workhorse/blob/main/docs/architecture.md#job_child).
