Automation API

Android device automation

Job Task Management

Utils

Manage job tasks, submit results, and request new tasks

Access job management through agent.utils.job. Used for managing job tasks, submitting results, and requesting new tasks.

JobUtils Interface
TypeScript
interface JobUtils {
submitTask(
automationStatus: "running" | "success" | "failed" | "declined",
data: Record<string, any>,
finish: boolean,
files: { name: string; extension: string; base64Data: string }[],
resultType?: ResultType
): Promise<{ success: false; error: string } | { success: true }>;
submitTask(options: {
automationStatus: "running" | "success" | "failed" | "declined";
data: Record<string, any>;
finish?: boolean;
files?: { name: string; extension: string; base64Data: string }[];
resultType?: ResultType;
}): Promise<{ success: false; error: string } | { success: true }>;
setResultType(resultType: ResultType): Promise<
{ success: false; error: string } |
{ success: true; result_type: Record<string, string>; job_price: number }
>;
submitTaskToAnotherJob(
job_id: string,
data: Record<string, any>,
status?: "pending" | "failed" | "declined",
files?: { name: string; extension: string; base64Data: string }[]
): Promise<
{ success: false; error: string } |
{ success: true; job_task_id: string; job_id: string }
>;
submitTaskToAnotherJob(options: {
job_id: string;
data: Record<string, any>;
status?: "pending" | "failed" | "declined";
files?: { name: string; extension: string; base64Data: string }[];
resultType?: ResultType;
}): Promise<
{ success: false; error: string } |
{ success: true; job_task_id: string; job_id: string }
>;
useAnotherTask(): Promise<{ job_task_id: string; job_proof: string } | null>;
getCurrentTask(): Promise<
{ success: false; error: string } |
{ success: true; parent_task_id: string; job_proof: any, timeout: number }
>;
addSubTasks(
job_variables: JobVariables[],
run_immediately?: boolean
): Promise<
{ success: false; error: string } |
{
success: true;
inserted_ids: { planned_task_id: string; input: string }[];
assignment_results?: { planned_task_id: string; assigned: boolean }[];
}
>;
addSubTasks(
job_variables: Record<string, any>[],
run_immediately: boolean,
job_id: string
): Promise<
{ success: false; error: string } |
{
success: true;
inserted_ids: { planned_task_id: string; input: string }[];
assignment_results?: { planned_task_id: string; assigned: boolean }[];
}
>;
addSubTasks(options: {
job_variables: Record<string, any>[];
run_immediately?: boolean;
job_id?: string;
remote_device_id?: string;
}): Promise<
{ success: false; error: string } |
{
success: true;
inserted_ids: { planned_task_id: string; input: string }[];
assignment_results?: { planned_task_id: string; assigned: boolean }[];
}
>;
getSubTasks(planned_task_ids: string[]): Promise<
{ success: false; error: string } |
{
success: true;
tasks: Record<string, {
parent_task_id: string | null;
comment: string | null;
job_proof: any | null;
timeout: number;
status: string;
}>;
}
>;
}
// On agent.utils (for task coordination)
interface AgentUtils {
setAutomationVariables(variables: object): Promise<
{ success: false; error: string } |
{ success: true; automation_variables: any }
>;
getAutomationVariables(): Promise<
{ success: false; error: string } |
{ success: true; automation_variables: any }
>;
}

submitTask()

TypeScript
submitTask(automationStatus: "running" | "success" | "failed" | "declined", data: Record<string, any>, finish?: boolean, files?: { name: string; extension: string; base64Data: string }[], resultType?: ResultType): Promise<...> | submitTask(options: { automationStatus; data; finish?; files?; resultType?: ResultType }): Promise<{ success: false; error: string } | { success: true }>

Submits the current task result to the server. Use this to report progress, success, or failure of job tasks. Note: Once you call submitTask with finish=true, you cannot submit again for the same task. The files parameter is ignored when finish=false. Also callable with a single options object ({ automationStatus, data, finish?, files?, resultType? }) — identical behaviour to the positional form, just easier to read when you only want to set a couple of fields (e.g. status + resultType). finish defaults to true when omitted.

Parameters

NameTypeDescription
automationStatus"running" | "success" | "failed" | "declined"Current status of the task
dataRecord<string, any>Task result data as key-value pairs
finishbooleanWhether this is the final submission for this task. Once true, no more submissions are allowed for this task.
files{ name, extension, base64Data }[]Array of files to upload with the task. Ignored when finish=false.
resultTypeResultTypeOptional result types ({field: value}) validated strictly against the automation's result_type_schema — an unknown field or a value outside the allowed list fails the whole submission. With a job task, it is stored on the task and re-derives the task's price from the job's result_type_pricing deltas (max(0, job_price + sum of recorded values' deltas)), recording a price_breakdown snapshot; without a job it is stored alongside the submitted data. Omitted fields fall back to their schema defaults.

Returns

{ success: true } | { success: false; error: string }Success status or error message

Examples

Options object with a result type
TypeScript
const result = await agent.utils.job.submitTask({
automationStatus: "success",
data: { orderId: "12345", totalAmount: 99.99 },
resultType: { "Account Type": "Old Account" },
// finish defaults to true; files defaults to []
});
if (result.success) {
console.log("Task submitted successfully");
}
Submit successful task with data
TypeScript
const result = await agent.utils.job.submitTask(
"success",
{
orderId: "12345",
totalAmount: 99.99,
itemsProcessed: 3
},
true, // Final submission
[] // No files
);
if (result.success) {
console.log("Task submitted successfully");
}
Submit task with files
TypeScript
const screenshot = await agent.actions.screenshot(1080, 1920, 80);
const result = await agent.utils.job.submitTask(
"success",
{ status: "completed" },
true, // Final submission
[{
name: "confirmation",
extension: "jpg",
base64Data: screenshot.screenshot || ""
}]
);
Report progress (not finished)
TypeScript
// Note: files parameter is ignored when finish=false
await agent.utils.job.submitTask(
"running",
{ currentStep: 3, totalSteps: 10 },
false, // More submissions coming
[] // Files ignored for progress updates
);
Decline a task
TypeScript
await agent.utils.job.submitTask(
"declined",
{ reason: "Item out of stock" },
true, // Final submission
[]
);

setResultType()

TypeScript
setResultType(resultType: ResultType): Promise<{ success: false; error: string } | { success: true; result_type: Record<string, string>; job_price: number }>

Reports the result type of the current job task (e.g. Account Type = "Old Account" vs "New Account"). The map is strictly validated on the server against the result_type_schema the automation developer defined in the project editor: unknown fields or values outside the allowed list are rejected, and omitted fields fall back to their schema defaults. When the job defines result_type_pricing, every value carries a price DELTA relative to the default price per task, and the task's price becomes max(0, job_price + sum of the recorded values' deltas), with a price_breakdown snapshot stored on the task. Only allowed while the task is still running — once finished, the task price is frozen for review and payout. Can be called multiple times; the last value before finishing wins. The generated ResultType interface (from the schema) gives you autocomplete for field names and values in the editor. Backed by POST /api/v2/tasks/setResultType. Requires running through a job task; when running without a job, pass resultType to submitTask instead.

Parameters

NameTypeDescription
resultTypeResultTypeResult type map, e.g. { "Account Type": "Old Account" }. Fields and values must match the automation's result_type_schema.

Returns

{ success: true; result_type; job_price } | { success: false; error: string }The normalized result type (defaults filled in) and the task price that was applied, or an error message

Examples

Report a result type during the run
TypeScript
const result = await agent.utils.job.setResultType({
"Account Type": "Old Account",
});
if (result.success) {
console.log("Task price is now", result.job_price);
}
Report the result type at final submission instead
TypeScript
await agent.utils.job.submitTask(
"success",
{ email: account.email },
true, // Final submission
[], // No files
{ "Account Type": "New Account" }
);

submitTaskToAnotherJob()

TypeScript
submitTaskToAnotherJob(job_id: string, data: Record<string, any>, status?: "pending" | "failed" | "declined", files?: { name: string; extension: string; base64Data: string }[]): Promise<...> | submitTaskToAnotherJob(options: { job_id; data; status?; files?; resultType?: ResultType }): Promise<{ success: false; error: string } | { success: true; job_task_id: string; job_id: string }>

Writes a task into a separate "data-dump" job from inside a running automation. The target job must be a devices_automation job that has automation_id set and no planned tasks; the running job and the target must share the same automation_id; and the running job's owner must own (or have shared_view / shared_edit access to) the target. Use this to push structured data — leads, scrape results, side-channel facts — into a sink job while the main automation keeps running. Differences from submitTask: • submitTask updates the currently running task; submitTaskToAnotherJob creates a brand-new task on a different job. • job_proof must be a JSON-serializable object (sent JSON-encoded). • Files use the {name, extension, base64Data} shape; their public URLs are inlined into the proof under proof[name] — no separate file fields. • status is the data-dump status: "pending" (default), "failed", or "declined". This is independent of the running automation status. • Skips manual-mode gates (rating / country / few_times / custom_vars / proof_files / positions / task_limit / Worker_Invites) and skips completion emails. • Does not affect the running task or the running automation's iteration / success-rate / blacklist analytics. Backed by POST /api/v2/tasks/submit (automation data-dump branch). Also callable with a single options object ({ job_id, data, status?, files?, resultType? }) — mirroring submitTask, the options form accepts resultType ({field: value}, strictly validated against the shared automation's result_type_schema; invalid values fail the submission). The created sink task stores the normalized result types plus a price_breakdown, and its price is derived from the TARGET job's result_type_pricing deltas (max(0, job_price + sum of recorded values' deltas)). Since the sink task is born in its final pre-review state, its price is fixed at creation.

Parameters

NameTypeDescription
job_idstringID of the target data-dump job (devices_automation, automation_id set, no planned tasks).
dataRecord<string, any>Proof data; serialized to JSON and stored as job_proof on the new task.
status"pending" | "failed" | "declined"Status of the new task on the target job. Defaults to "pending".
files{ name, extension, base64Data }[]Optional files to attach. Each file is written to disk and its public URL is embedded into the JSON proof under proof[name].
resultTypeResultTypeOptions-object overload only. Result types ({field: value}) validated strictly against the shared automation's result_type_schema — invalid values fail the submission. Stored on the created sink task with a price_breakdown; the task's price derives from the TARGET job's result_type_pricing deltas.

Returns

{ success: true; job_task_id: string; job_id: string } | { success: false; error: string }On success, the created task and target job ids; otherwise an error message.

Examples

Options object with a result type
TypeScript
const result = await agent.utils.job.submitTaskToAnotherJob({
job_id: "68868530c189957861cd698a",
data: { email: account.email, password: account.password },
status: "pending",
resultType: { "Account Type": "Old Account" },
});
if (result.success) {
console.log("Sink task", result.job_task_id);
}
Push structured data into a sink job
TypeScript
const result = await agent.utils.job.submitTaskToAnotherJob(
"68868530c189957861cd698a", // target data-dump job_id
{
email: "lead@example.com",
name: "Jane Doe",
capturedAt: Date.now(),
},
);
if (result.success) {
console.log("Wrote dump task", result.job_task_id, "to job", result.job_id);
}
Attach a screenshot to the dump
TypeScript
const screenshot = await agent.actions.screenshot(1080, 1920, 80);
await agent.utils.job.submitTaskToAnotherJob(
"68868530c189957861cd698a",
{ note: "Profile page state at capture" },
"pending",
[{
name: "snapshot", // proof.snapshot will hold the public URL
extension: ".jpg",
base64Data: screenshot.screenshot || "",
}],
);
Mark a dump as failed
TypeScript
await agent.utils.job.submitTaskToAnotherJob(
"68868530c189957861cd698a",
{ reason: "captcha blocked" },
"failed",
);

useAnotherTask()

TypeScript
useAnotherTask(): Promise<{ job_task_id: string; job_proof: string } | null>

Accesses another task's data from the same job. This allows one job task to retrieve and use data from another task within the same job. The target task must have set its automation variables with waiting: true using setAutomationVariables to be discoverable. Returns the task details or null if no waiting task is available.

Returns

{ job_task_id: string; job_proof: string } | nullTask details from another waiting task, or null if no task is available

Examples

Access data from another task in the same job
TypeScript
// First, the other task must mark itself as waiting:
// await agent.utils.setAutomationVariables({ waiting: true });
// Then this task can access it:
const otherTask = await agent.utils.job.useAnotherTask();
if (otherTask) {
console.log("Found waiting task:", otherTask.job_task_id);
const proof = JSON.parse(otherTask.job_proof);
// Use data from the other task
const sharedData = proof.someSharedField;
// Process with the shared data...
} else {
console.log("No waiting task available");
}
Coordinate between multiple tasks
TypeScript
// Task A: Set up data and wait
await agent.utils.job.submitTask(
"running",
{ preparedData: "value", step: "waiting" },
false,
[]
);
await agent.utils.setAutomationVariables({ waiting: true });
// Task B: Access Task A's data
const taskA = await agent.utils.job.useAnotherTask();
if (taskA) {
const taskAData = JSON.parse(taskA.job_proof);
console.log("Got data from Task A:", taskAData.preparedData);
}

getCurrentTask()

TypeScript
getCurrentTask(): Promise<{ success: false; error: string } | { success: true; parent_task_id: string; job_proof: any, timeout: number }>

Gets information about the currently assigned task, including the parent task ID and job proof data.

Returns

{ success: true; parent_task_id: string; job_proof: any, timeout: number } | { success: false; error: string }Current task details or error

Examples

TypeScript
const task = await agent.utils.job.getCurrentTask();
if (task.success) {
console.log("Current task ID:", task.parent_task_id);
console.log("Job proof:", task.job_proof);
// Access job proof data
const targetUrl = task.job_proof.url;
const credentials = task.job_proof.credentials;
} else {
console.log("Error getting task:", task.error);
}

addSubTasks()

TypeScript
addSubTasks(job_variables: JobVariables[], run_immediately?: boolean): Promise<...>

Creates sub-tasks (planned tasks) for the current job or another job owned by the same employer. Each item in the job_variables array becomes a separate planned task. When job_id is omitted, the current task's job ID is used automatically (and cached for subsequent calls). When job_id targets a different job, the parent task's job owner must own or have shared_edit access to the target job, and the target job must have allow_sub_tasks enabled. Supports three call signatures: positional arguments with JobVariables[], positional arguments with a custom job_id, or a single options object for full control (including remote_device_id).

Parameters

NameTypeDescription
job_variablesJobVariables[] | Record<string, any>[]Array of job variable objects. Each object becomes the input for one sub-task. Uses JobVariables type (matching the automation's schema) when targeting the current job, or generic Record<string, any> when a custom job_id is provided.
run_immediatelybooleanWhether to attempt immediate task assignment after creation. Defaults to true.
job_idstringTarget job ID. If omitted, uses the current task's job ID (fetched once and cached). When targeting a different job, the parent task's owner must own or have shared_edit access to it, and the target job must have allow_sub_tasks enabled.
remote_device_idstringPin sub-tasks to a specific device by its remote_device_id. Only available via the options object overload.

Returns

{ success: true; inserted_ids: { planned_task_id: string; input: string }[]; assignment_results?: { planned_task_id: string; assigned: boolean }[] } | { success: false; error: string }Inserted planned task IDs with their inputs, and optional assignment results when run_immediately is true

Examples

Create sub-tasks for the current job
TypeScript
const result = await agent.utils.job.addSubTasks([
{ email: "user1@example.com", action: "verify" },
{ email: "user2@example.com", action: "verify" },
]);
if (result.success) {
console.log("Created sub-tasks:", result.inserted_ids);
// Save planned_task_ids to check status later
const ids = result.inserted_ids.map(t => t.planned_task_id);
}
Create sub-tasks without immediate assignment
TypeScript
const result = await agent.utils.job.addSubTasks(
[{ url: "https://example.com/page1" }, { url: "https://example.com/page2" }],
false // Don't assign immediately
);
Create sub-tasks for a different job
TypeScript
const result = await agent.utils.job.addSubTasks(
[{ targetId: "abc123" }],
true,
"674a1b2c3d4e5f6a7b8c9d0e" // Different job ID
);
Options object — pin sub-tasks to a specific device
TypeScript
const result = await agent.utils.job.addSubTasks({
job_variables: [
{ query: "search term 1" },
{ query: "search term 2" },
],
run_immediately: true,
remote_device_id: "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
});
Options object — all options
TypeScript
const result = await agent.utils.job.addSubTasks({
job_variables: [{ action: "scrape", url: "https://example.com" }],
run_immediately: false,
job_id: "674a1b2c3d4e5f6a7b8c9d0e",
remote_device_id: "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
});

getSubTasks()

TypeScript
getSubTasks(planned_task_ids: string[]): Promise<...>

Gets the status and details of sub-tasks by their planned task IDs. Returns a map keyed by planned_task_id. Only returns data for planned tasks whose parent_task_id matches the current task. Status is "planned" if not yet assigned to a device, "deleted" if the job task was removed, or the actual job task status (e.g. "running", "confirmed", "failed") otherwise.

Parameters

NameTypeDescription
planned_task_idsstring[]Array of planned task IDs to query (max 100). These should be IDs returned from addSubTasks.

Returns

{ success: true; tasks: Record<string, { parent_task_id, comment, job_proof, timeout, status }> } | { success: false; error: string }Map of planned_task_id to task details. Status is "planned", "deleted", or the job task status.

Examples

Check status of sub-tasks
TypeScript
// After creating sub-tasks with addSubTasks
const result = await agent.utils.job.getSubTasks([
"674a1b2c3d4e5f6a7b8c9d01",
"674a1b2c3d4e5f6a7b8c9d02",
]);
if (result.success) {
for (const [plannedTaskId, task] of Object.entries(result.tasks)) {
console.log(`Task ${plannedTaskId}: status=${task.status}`);
if (task.status === "confirmed") {
console.log("Result:", task.job_proof);
}
}
}
Poll sub-tasks until all complete
TypeScript
const subTaskResult = await agent.utils.job.addSubTasks([
{ query: "search term 1" },
{ query: "search term 2" },
]);
if (!subTaskResult.success) throw new Error(subTaskResult.error);
const ids = subTaskResult.inserted_ids.map(t => t.planned_task_id);
// Poll until all sub-tasks finish
while (true) {
const status = await agent.utils.job.getSubTasks(ids);
if (!status.success) break;
const allDone = Object.values(status.tasks).every(
t => ["confirmed", "failed", "declined", "deleted"].includes(t.status)
);
if (allDone) {
console.log("All sub-tasks completed");
break;
}
await sleepRandom(5000, 10000);
}

Automation Variables

These methods are accessed via agent.utils (not agent.utils.job) but are used for coordinating between job tasks.

setAutomationVariables()

TypeScript
setAutomationVariables(variables: object): Promise<{ success: false; error: string } | { success: true; automation_variables: any }>

Sets automation variables for the current job task. These variables can be used to coordinate between tasks in the same job. Set { waiting: true } to make the current task's data available to other tasks via useAnotherTask().

Parameters

NameTypeDescription
variablesobjectAn object containing the variables to set. Use { waiting: true } to mark this task as available for other tasks to access.

Returns

{ success: true; automation_variables: any } | { success: false; error: string }Success status with the stored variables, or error message

Examples

Mark task as waiting for another task
TypeScript
// Make this task's data available for another task to use
await agent.utils.setAutomationVariables({ waiting: true });
// Now another task in the same job can call useAnotherTask()
// to access this task's job_proof data
Store custom variables for task coordination
TypeScript
// Store state that can be retrieved later
const result = await agent.utils.setAutomationVariables({
waiting: true,
stage: "data_prepared",
timestamp: Date.now()
});
if (result.success) {
console.log("Variables set:", result.automation_variables);
} else {
console.error("Failed to set variables:", result.error);
}

getAutomationVariables()

TypeScript
getAutomationVariables(): Promise<{ success: false; error: string } | { success: true; automation_variables: any }>

Retrieves the automation variables previously set for the current job task. Use this to check the current state of task coordination variables.

Returns

{ success: true; automation_variables: any } | { success: false; error: string }Success status with the stored variables, or error message

Examples

Check current automation variables
TypeScript
const result = await agent.utils.getAutomationVariables();
if (result.success) {
console.log("Current variables:", result.automation_variables);
if (result.automation_variables?.waiting) {
console.log("This task is marked as waiting");
}
} else {
console.error("Failed to get variables:", result.error);
}
Retrieve stored coordination state
TypeScript
const result = await agent.utils.getAutomationVariables();
if (result.success && result.automation_variables) {
const { stage, timestamp } = result.automation_variables;
console.log(`Task is at stage: ${stage}, set at: ${timestamp}`);
}