Developer
News and Updates
Get Support
Sign in
Get Support
Sign in
DOCUMENTATION
Cloud
Data Center
Resources
Sign in
Sign in
DOCUMENTATION
Cloud
Data Center
Resources
Sign in
Object types
Object operations
User operations
Group operations
Task operations
Last updated May 28, 2026

scheduleChildTask

The scheduleChildTask method schedules a one-off child task from inside a running taskRunner invocation. The platform delivers each child back to the same taskRunner as a separate invocation, allowing you to break large work units into smaller pieces that run independently and in parallel.

Use this method to walk a tree, drain a paginated list, or split a batch into per-chunk invocations. The child runs once when the platform picks it up; it does not recur.

Method signature

1
2
scheduleChildTask(request: ChildTaskScheduleRequest): Promise<ChildTaskScheduleResponse>

Parameter: request

  • Type: ChildTaskScheduleRequest
  • Required: Yes
  • Description: The child task schedule request, threaded with the current scan and execution context.
1
2
{
  scanId: string;          // From the current TaskRunnerPayload
  taskExecutionId: string; // From the current TaskRunnerPayload
  task: ChildTask;         // The child task to schedule
  connectionId: string;    // From the current TaskRunnerPayload
}

ChildTask

1
2
{
  parentTaskId: string;          // The taskId of the invocation calling scheduleChildTask
  taskType: ForgeTaskType;       // One of types.FORGE_TASK_TYPES (closed enum)
  taskId: string;                // Deterministic UUID for this child
  temporalTaskMetadata?: any;    // Optional, platform-only payload
  persistentTaskMetadata?: any;  // Optional, platform-only payload
}

The scanId, taskExecutionId, and connectionId fields are taken from the TaskRunnerPayload your handler received. They thread the child into the same scan as the parent so the platform can correlate the fan-out tree.

Child tasks typically share their parent's taskType. The same taskRunner routine handles both the root tick and the per-item children, with TaskInfo.metadata carrying the per-invocation context (for example, the specific folder being walked). See Task types.

Usage examples

Fan out a folder walk

1
2
import { graph, types } from '@forge/teamwork-graph';
import { v5 as uuidv5 } from 'uuid';
import { kvs } from '@forge/kvs';

const TASK_NAMESPACE = uuidv5('my-connector', uuidv5.URL);

interface FolderMetadata {
  folderId: string;
  pageToken?: string;
}

async function scheduleFolderDiscovery(
  request: TaskRunnerPayload,
  meta: FolderMetadata,
): Promise<void> {
  // Deterministic id keyed by (scanId, parent, work unit). Re-deliveries of
  // the parent produce the same childTaskId so every duplicate runner invocation
  // lands on the same TaskInfo row and short-circuits via completed: true.
  const childKey = `folder:${meta.folderId}:page:${meta.pageToken ?? '0'}`;
  const childTaskId = uuidv5(
    `${request.scanId}:${request.taskId}:${childKey}`,
    TASK_NAMESPACE,
  );

  // Persist the per-invocation context the child will need. Read it back
  // inside taskRunner using the child's taskId.
  await kvs.set(`taskInfo:${childTaskId}`, {
    taskType: types.FORGE_TASK_TYPES.ENTITY_INGESTION_INCREMENTAL,
    connectionId: request.connectionId,
    metadata: meta,
  });

  const response = await graph.scheduleChildTask({
    scanId: request.scanId,
    taskExecutionId: request.taskExecutionId,
    connectionId: request.connectionId,
    task: {
      parentTaskId: request.taskId,
      taskType: types.FORGE_TASK_TYPES.ENTITY_INGESTION_INCREMENTAL,
      taskId: childTaskId,
    },
  });

  if (response.error) {
    console.error('Failed to schedule child task:', response.error);
  }
}

Recurse through pagination

1
2
async function executeFolderDiscovery(
  request: TaskRunnerPayload,
  meta: FolderMetadata,
): Promise<void> {
  const page = await listFolderPage(meta.folderId, { pageToken: meta.pageToken });

  // Schedule a child per subfolder
  for (const sub of page.subfolders) {
    await scheduleFolderDiscovery(request, { folderId: sub.id });
  }

  // Continue pagination by self-scheduling for the next page
  if (page.nextPageToken) {
    await scheduleFolderDiscovery(request, {
      folderId: meta.folderId,
      pageToken: page.nextPageToken,
    });
  }
}

Derive each child's taskId deterministically (for example with uuidv5(scanId + parentTaskId + childKey)) so re-deliveries of the parent reuse the same child taskId. The platform creates a fresh task execution for every scheduleChildTask call regardless of the taskId you pass; deterministic IDs are how your taskRunner recognizes a redelivery and short-circuits via the TaskInfo.completed check. Random UUIDs make every retried child look like new work to your runner, so the full pipeline (third-party API calls, setObjects, etc.) runs again on every retry. See At-least-once delivery and idempotency.

temporalTaskMetadata and persistentTaskMetadata are platform-only; their propagation back to taskRunner is not a stable contract today. Use KVS keyed by the child taskId for any per-invocation context your handler needs.

Request validation

The method validates the following:

  • connectionId: Required, must be a non-empty string.
  • task.parentTaskId: Required, must be a non-empty string. Pass the taskId of the invocation calling scheduleChildTask.
  • task.taskType: Required, non-empty, max 255 characters, and must be one of the values exposed by types.FORGE_TASK_TYPES.
  • task.taskId: Required, must be a valid UUID. Derive deterministically to make the call idempotent under at-least-once delivery.

The scanId and taskExecutionId fields are not validated by the SDK client-side; they are validated server-side. Pass them through unchanged from the current TaskRunnerPayload.

Error handling

Error messageDescription
connectionId is requiredThe connectionId is missing or empty.
task.parentTaskId is requiredThe task.parentTaskId field is missing. Pass request.taskId from the current TaskRunnerPayload.
task.taskType is requiredThe task.taskType is missing or empty.
task.taskType must not exceed 255 charactersThe task.taskType is longer than the 255-character limit.
task.taskType must be one of: user_ingestion_full, user_ingestion_incremental, ...The task.taskType is not one of the values exposed by types.FORGE_TASK_TYPES.
task.taskId must be a valid UUID formatThe provided child taskId is not a valid UUID.
Failed to schedule child tasks: Not Found - Scan not foundServer-side: the scanId does not match an active scan on the connection. Common when reusing a stale scanId outside of an active taskRunner invocation.

Response

The method returns a promise that resolves to a ChildTaskScheduleResponse.

1
2
{
  status: string;          // Status string from the platform (e.g. "ACCEPTED")
  message: string;         // Human-readable status message
  error?: string;          // Error message if the request failed
  originalError?: unknown; // Original platform error object, when available
}

On success, the response carries status and message. On failure, the SDK returns an object with error and originalError populated. Branch on response.error to detect failures. The child taskId you submitted is yours to track in KVS; the response does not echo it.

Type safety

The SDK provides type-safe request and response objects that ensure compile-time validation:

1
2
import { graph, types } from '@forge/teamwork-graph';

const request: types.ChildTaskScheduleRequest = {
  scanId: incomingPayload.scanId,
  taskExecutionId: incomingPayload.taskExecutionId,
  connectionId: incomingPayload.connectionId,
  task: {
    parentTaskId: incomingPayload.taskId,
    taskType: types.FORGE_TASK_TYPES.ENTITY_INGESTION_INCREMENTAL,
    taskId: 'a1b2c3d4-5e6f-7a8b-9c0d-1e2f3a4b5c6d',
  },
};

const response: types.ChildTaskScheduleResponse = await graph.scheduleChildTask(request);

if (response.error) {
  console.error('Failed to schedule child task:', response.error);
}

The type system ensures:

  • task.taskType is constrained to the ForgeTaskType union exposed by types.FORGE_TASK_TYPES.
  • request has the correct ChildTaskScheduleRequest structure with the scan and execution context wired in.
  • response is properly typed as ChildTaskScheduleResponse with optional error and originalError.

Rate this page: