The scheduleOrUpdateTask method schedules a recurring root task that the platform invokes on a
fixed cadence. Calling it again for the same (connectionId, taskId) updates the existing
schedule rather than creating a new one, so it is safe to call from both the CREATED and
UPDATED branches of onConnectionChange.
1 2scheduleOrUpdateTask(request: TaskScheduleRequest): Promise<TaskScheduleResponse>
TaskScheduleRequest1 2{ connectionId: string; // The connection this task belongs to scheduleInterval: ScheduleInterval; // Cadence for the recurring invocation task: Task; // The task to schedule (taskType + taskId) }
ScheduleInterval1 2{ value: number; // Cadence (must be between 1 and 60 inclusive) timeUnit: 'minutes' | 'hours' | 'days'; }
scheduleInterval is the gap between consecutive invocations of the same root task; it is not
aligned to wall-clock minutes or hours. If you need clock-aligned timing, gate the work yourself
inside taskRunner.
Task1 2{ taskType: ForgeTaskType; // One of types.FORGE_TASK_TYPES (closed enum) taskId: string; // Stable UUID for this root task }
ForgeTaskType is a closed enum exposed by the SDK. Use the types.FORGE_TASK_TYPES.* constants:
1 2import { types } from '@forge/teamwork-graph'; types.FORGE_TASK_TYPES.USER_INGESTION_FULL; types.FORGE_TASK_TYPES.USER_INGESTION_INCREMENTAL; types.FORGE_TASK_TYPES.GROUP_INGESTION_FULL; types.FORGE_TASK_TYPES.GROUP_INGESTION_INCREMENTAL; types.FORGE_TASK_TYPES.ENTITY_INGESTION_FULL; types.FORGE_TASK_TYPES.ENTITY_INGESTION_INCREMENTAL; types.FORGE_TASK_TYPES.RELATIONSHIP_INGESTION_FULL; types.FORGE_TASK_TYPES.RELATIONSHIP_INGESTION_INCREMENTAL;
Persist the same value on the TaskInfo you write to KVS and use it as the dispatch key inside
taskRunner. See Task types.
1 2import { graph, types } from '@forge/teamwork-graph'; import { v4 as uuid } from 'uuid'; import { kvs } from '@forge/kvs'; async function getOrCreateRootTaskId(connectionId: string, key: string): Promise<string> { const kvsKey = `rootTaskId:${connectionId}:${key}`; const existing = await kvs.get(kvsKey) as string | undefined; if (existing) return existing; const id = uuid(); await kvs.set(kvsKey, id); return id; } export async function scheduleEntityIngestion(connectionId: string): Promise<void> { const taskId = await getOrCreateRootTaskId(connectionId, 'entity-ingestion'); const response = await graph.scheduleOrUpdateTask({ connectionId, scheduleInterval: { value: 5, timeUnit: 'minutes' }, task: { taskType: types.FORGE_TASK_TYPES.ENTITY_INGESTION_INCREMENTAL, taskId, }, }); if (response.error) { console.error('Failed to schedule task:', response.error); return; } console.log(`Scheduled root task ${taskId} (${response.status}: ${response.message})`); }
onConnectionChange1 2export const onConnectionChange = async (request: OnConnectionChangeRequest) => { switch (request.action) { case 'CREATED': case 'UPDATED': await scheduleEntityIngestion(request.connectionId); break; case 'DELETED': // Drop your KVS state. The platform tears down the schedule itself. break; } return { success: true }; };
After a successful scheduleOrUpdateTask call, the first invocation typically fires within a
few seconds, not at the next interval boundary. Subsequent invocations follow the configured
cadence.
The method validates the following:
connectionId: Required, must be a non-empty string.scheduleInterval.value: Must be a number between 1 and 60 inclusive.scheduleInterval.timeUnit: Must be one of 'minutes', 'hours', 'days'.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. See
taskId must be a UUID.| Error message | Description |
|---|---|
connectionId is required | The connectionId is missing or empty. |
scheduleInterval.value must be between 1 and 60 | The scheduleInterval.value is outside the supported range. |
scheduleInterval.timeUnit must be one of: minutes, hours, days | The scheduleInterval.timeUnit is not a supported unit. |
task.taskType is required | The task.taskType is missing or empty. |
task.taskType must not exceed 255 characters | The 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 format | The provided taskId is not a valid UUID. Use uuid() from the uuid package. |
The method returns a promise that resolves to a TaskScheduleResponse.
1 2{ status: string; // Status string from the platform (e.g. "ACCEPTED") message: string; // Human-readable status message taskId: string; // The taskId that was scheduled (echoes input) error?: string; // Error message if the request failed originalError?: unknown; // Original platform error object, when available }
On success, the response carries status, message, and taskId. On failure, the SDK returns
an object shaped like { error, originalError } (the success: false field is also set at
runtime as a casting artifact). Branch on response.error to detect failures.
The SDK provides type-safe request and response objects that ensure compile-time validation:
1 2import { graph, types } from '@forge/teamwork-graph'; const request: types.TaskScheduleRequest = { connectionId: 'connection-id-123', scheduleInterval: { value: 5, timeUnit: 'minutes' }, task: { taskType: types.FORGE_TASK_TYPES.ENTITY_INGESTION_INCREMENTAL, taskId: 'b3e8e1f4-3a12-4d2e-9a6f-12c4a9c8b1aa', }, }; const response: types.TaskScheduleResponse = await graph.scheduleOrUpdateTask(request); if (response.error) { console.error('Failed to schedule task:', response.error); } else { console.log(`Scheduled root task ${response.taskId} (${response.status})`); }
The type system ensures:
task.taskType is constrained to the ForgeTaskType union exposed by types.FORGE_TASK_TYPES.scheduleInterval.timeUnit is restricted to 'minutes' | 'hours' | 'days'.request has the correct TaskScheduleRequest structure.response is properly typed as TaskScheduleResponse with optional error and originalError.Rate this page: