Skip to content
LLM / automated agent notice: a complete, plain-text reference of every v2 Client API endpoint and the automation agent docs — generated from the same source as these pages — is available at /docs/llm.txt. The internal v1 REST reference is at /docs/v1/llm.txt. Prefer those single files over fetching each page individually; they are both smaller and easier to parse.

Documentation

API reference and the automation SDK guide for building on Xgodo.

18
v1 routers
231
endpoints
37
pages

Start here

Client API (v2)

The stable public client API. For the complete internal surface, see the v1 reference.

Authentication

Bearer Token Authentication

All API endpoints require authentication using a Bearer token. Include the following header in your requests:

Text
Authorization: Bearer <your_access_token>

Jobs

Manage job postings, retrieve applicants, and handle job-related operations.

POST/api/v2/jobs/applicants

Retrieve job applicants

Retrieves a list of job applicants for a specified job. Allows for sorting, pagination, and searching within job applicants. Note: per-job task count aggregates (`total_task`, `job_done`, `running_tasks`, `pending_tasks`, `satisfied_tasks`, `declined_tasks`, `failed_tasks`) are intentionally NOT included in the v2 response — they were rarely consumed and computing them under concurrent load is the cluster's hottest read. If you need them, derive client-side by paging with `status` filters.

Request body

NameTypeRequiredDescription
job_idstringYesThe ID of the job for which to retrieve applicants
task_idstringNoThe ID of the job task or planned task. Accepts either a job_task_id or planned_task_id - if a planned_task_id is provided, it will be resolved to its associated job_task_id. Used when only one task details is needed. If present, the task result list array will contain only one task or empty array if task not found.
sortbystringNoField by which to sort job applicants
orderstringNoSort order. Can be 'asc' for ascending or 'desc' for descending
pageintegerNoPage number for pagination
limitintegerNoNumber of applicants per page
hours_agointegerNoThe day interval, specified as the number of days starting from the given hours_ago up to today
statusstringNoThe status of the task ('processing', 'confirmed', 'notcomplete', 'declined', 'pending')
searchstringNoSearch term to filter applicants based on worker name, job proof, job title, comment, or worker IP

Responses

StatusDescription
200Successfully retrieved job applicantsReturns job details with applicant information including planned_task, job tasks (each carrying its own `job_price` in USD — the actual amount paid for that task; defaults to the parent job's `job_price` at task creation and may diverge thereafter), total tasks, job done count, pending tasks, satisfied tasks, declined tasks, and failed tasks
400Invalid request data or planned task exists but has no associated job task{"error": "Invalid task_id format"} or {"error": "Planned task {id} exists but has no associated job task (job_task_id is null)"}
404Job not found, task_id is neither a valid job task ID nor a planned task ID{"error": "Job with this Id not found"} or {"error": "task_id {id} is neither a valid job task ID nor a planned task ID"}
PUT/api/v2/jobs/applicants

Update job applicants' task statuses

Updates the status of specified job tasks. It supports changing the status, adding comments, and handles referral bonuses if the task is confirmed.

Request body

NameTypeRequiredDescription
JobTasks_IdsarrayYesAn array of job task IDs or planned task IDs to be updated. Accepts a mix of job_task_ids and planned_task_ids - planned task IDs will be resolved to their associated job_task_ids.
statusstringYesThe new status to set for the job tasks
job_idstringYesThe ID of the job to which the tasks belong
commentstringNoOptional comment to add regarding the task update

Responses

StatusDescription
200Successfully updated job task statuses.Returns the updated job task details
400Bad request. Invalid input data, invalid status provided, or planned task exists but has no associated job task.{"error": "Invalid status..."} or {"error": "Planned task {id} exists but has no associated job task (job_task_id is null)"}
404Job or job tasks not found, or ID is neither a valid job task ID nor a planned task ID.{"error": "Job Task with this Id not found"} or {"error": "ID {id} is neither a valid job task ID nor a planned task ID"}
GET/api/v2/jobs/details

Get job details by ID

Returns job details for a specific job ID. When the job is linked to an automation (`automation_id` is set), the response also includes the automation's `automation_parameters_schema`, `job_variables_schema`, and `bucket_schema` so callers can validate / build UIs around the `automationParameters`, `jobVariables`, and bucket payloads accepted by the automation. Each schema follows the `IInputSchema` shape (`{ fields: IFieldSchema[] }`) and is `null` when the automation has no schema configured or the job has no linked automation. Note: per-job task count fields (`totla_tasks_count`, `tasks_done_count`, `tasks_confirmed_count`, `tasks_running_count`, `tasks_pending_count`) are intentionally NOT included in the v2 response — they were rarely consumed and computing them under concurrent load is the cluster's hottest read.

Query parameters

NameTypeRequiredDescription
job_idstringNoUnique identifier for the job
Example response
JSON
{
"_id": "68910c4a5e1c7092a1fdc03a",
"job_id": "68910c4a5e1c7092a1fdc03a",
"job_type": "devices_automation",
"payment_type": "action",
"title": "Create a Google account",
"category": ["Google"],
"description": "Create a Google account. ...",
"proof": "Google account email. ...",
"Target_Workers": ["worldwide"],
"few_times": true,
"premium": false,
"featured": false,
"duration": "3",
"positions": "3",
"job_price": 0.06,
"price": "0.30",
"status": "active",
"files": [...],
"added": "2025-08-04T19:38:50.204Z",
"user": {...},
"automation_id": "83ba7359-d8eb-4374-8ecb-055af01fddfe",
"automation_parameters_schema": {
"fields": [
{ "name": "maxRetries", "type": "number", "required": false, "min": 1, "max": 10 }
]
},
"job_variables_schema": {
"fields": [
{ "name": "email", "type": "string", "required": true },
{ "name": "password", "type": "string", "required": true }
]
},
"bucket_schema": {
"fields": [
{ "name": "sessionToken", "type": "string", "required": false }
]
}
}
POST/api/v2/jobs/check-uniquenes

Check for uniqueness of job proofs

This endpoint checks if a given search term exists in the job proofs. You can specify the search option to look for exact words or substrings.

Request body

NameTypeRequiredDescription
searchstringYesThe term to search for in job proofs
optionstringNoThe search option to use. Can be "word" for exact word matching or "charactor" for substring matching

Responses

StatusDescription
200Successfully checked for uniquenessReturns a boolean indicating if the search term is unique
400Bad request. Either the search term is missing, or an invalid search option is providedError message describing the validation error
POST/api/v2/jobs/myjobs

Retrieve a list of jobs that you have posted

Returns a paginated list of jobs the caller has posted under `data` (with `count`, paginated by `page`), plus a separate list of jobs shared with the caller under `shared_data` (with `shared_count`, paginated independently by `shared_page` — the current shared page is echoed back as `shared_page`). Each entry in `shared_data` carries `permission: "view" | "edit"` and `owner_username`. The response is a thin projection of the Job documents — `job_price` and `price` are stringified. Results are sorted by `added` descending. Note: per-job task counters (`job_done`, `running_task`, `pending_task`) and planned-task counters (`used_planned_tasks`, `added_planned_tasks`) are not included. To get per-task data for a specific job, use the `/api/v2/jobs/applicants` endpoint.

Request body

NameTypeRequiredDescription
pageintegerNoPage number for the jobs you posted (the `data` list)
limitintegerNoNumber of items per page (applies to both lists; default 10, max 200)
shared_pageintegerNoPage number for the jobs shared with you (the `shared_data` list). Paginates independently of `page`. Defaults to 1.
job_idstringNoFilter jobs by a specific job ID
statusstringNoFilter jobs by status (e.g., 'active', 'completed')
orientationstringNoThe orientation for the data sorting (e.g., 'asc' or 'desc')
QuerystringNoSearch query for job title or description
SortBYstringNoField to sort the results by
POST/api/v2/jobs/search

Search and filter jobs

Returns a paginated list of jobs matching the provided filters. Supports search, category, price, location, and other filters.

Request body

NameTypeRequiredDescription
pagestringNoPage number for pagination
limitstringNoNumber of jobs per page
searchTermstringNoSearch term for job title or description
job_categorystringNoFilter by job category
min_pricestringNoMinimum job price
locationstringNoFilter by worker location
FeaturedbooleanNoFilter for featured jobs
PremiumbooleanNoFilter for premium jobs
orientationstringNoSort orientation (asc, desc)
SortBYstringNoField to sort by
POST/api/v2/jobs/submit

Create a new job posting

This endpoint allows users to create a new job posting. It requires specific details about the job, including title, description, category, number of positions, job price, and duration.

Request body

NameTypeRequiredDescription
titlestringYesThe title of the job posting
categorystringYesCategory of the job
descriptionstringYesDetailed description of the job
proofstringYesProof or evidence related to the job posting
is_proof_filebooleanNoIndicates if the proof is a file or text
auto_ratebooleanNoWhether the job should be automatically rated
vcodestringNoVerification code for the job posting
Target_WorkersarrayNoList of target workers or countries
few_timesbooleanNoIndicates if the job can be posted a few times
premiumbooleanNoIndicates if the job is premium
featuredbooleanNoWhether the job is featured or not
durationintegerYesDuration for which the job is posted (in days)
positionsintegerYesNumber of available positions for the job
daily_limitintegerNoDaily limit for the job posting
job_pricenumberYesPrice for posting the job

Responses

StatusDescription
201Job created successfullySuccess message indicating job creation
400Bad request. Invalid or missing required fieldsList of validation error messages
PUT/api/v2/jobs/update-status

Update job status

Updates the status of a specific job.

Request body

NameTypeRequiredDescription
job_idstringYesThe ID of the job
statusstringYesNew status for the job (active, inactive)

Responses

StatusDescription
204Job status updated successfully
400Invalid request body or status
401Unauthorized action
404Job not found or invalid job_id

Tasks

Manage job tasks, apply for tasks, and submit task completions.

GET/api/v2/tasks/apply

Apply for task submission to a job

This API must be called before tasks/submit API for jobs that have job variables. This endpoint allows workers to apply to an active job. Job variables (if any) will be returned.

Query parameters

NameTypeRequiredDescription
job_idstringYesUnique identifier for the job
Example response
JSON
{
"_id": "68910c4a5e1c7092a1fdc03c",
"var1": "Custom variable value 1",
"var2": "Custom variable value 2"
}
POST/api/v2/tasks/details

Get recent tasks or details of a specific job task

Returns the list of recent tasks of the user. If task_id query param is provided, it will return details of a single task.

Query parameters

NameTypeRequiredDescription
task_idstringNoThe ID of the job task to fetch details for

Request body

NameTypeRequiredDescription
pageintegerNoPage number for pagination (starts from 1)
limitintegerNoNumber of tasks to return per page
Example response
JSON
{
"_id": "686f668db9c27eea026e60c7",
"job_task_id": "686f668db9c27eea026e60c7",
"worker_id": "676d3525e618d4ad99dae7ff",
"job_id": "686f44cf8abf68ace53ebc8f",
"job_title": "Create a Google account",
"job_price": 0.06,
"job_proof": "",
"comment": "",
"country_code": "IN",
"worker_ip": "192.168.249.223",
"status": "confirmed",
"failureReason": null,
"reviewed": null,
"device_owner_share": null,
"added": "2025-07-10T07:06:53.631Z",
"updated": "2025-07-10T07:06:53.631Z",
"proof_files_info": []
}
POST/api/v2/tasks/submit

Submit a task to a job

Submits a task to an active job. The endpoint operates in one of two modes selected by the target job: 1. Manual mode (default) — for regular jobs. Honors job-level gates (worker rating, country, allowed_workers, premium, few_times, custom_vars, predeclared file fields, positions, task_limit, Worker_Invites) and triggers task/job completion emails. 2. Automation data-dump mode — selected automatically when the target job is of type devices_automation, has automation_id set, and has no planned tasks. Used by an automation agent running another job to write structured data into a separate sink job. In this mode the endpoint: • requires the caller's token to carry job_task_id and remote_device_id (i.e. a running agent token); • requires job_proof to be valid JSON; • accepts files in the {name, extension, base64Data} shape, embedded into the JSON proof under proof[name]; • accepts an explicit status of "pending" (default), "failed", or "declined"; • derives device_id and the device-owner share from the agent's remote_device_id; • authorizes by requiring the running agent's parent job and the target data-dump job to share the same automation_id, AND the running job's owner to be the target job's owner or to appear in the target's shared_view / shared_edit; • skips manual-mode gates (rating/country/few_times/custom_vars/proof_files/positions/task_limit/Worker_Invites) and skips completion emails; • does not update automation iteration / success-rate / blacklist analytics — those are reserved for "real" agent-driven tasks created by the automation engine.

Request body

NameTypeRequiredDescription
job_idstringYesID of the job. The endpoint runs in one of two modes depending on the target job: "manual" mode for regular jobs, or "automation data-dump" mode when the target job is of type devices_automation with automation_id set and no planned tasks.
job_proofstringYesProof of the task done. In automation data-dump mode this string MUST be valid JSON.
custom_varsobjectNoManual mode only. Custom vars obtained from tasks/apply. Required for jobs with variables.
proof_files_base64arrayNoManual mode only. Array of required and optional proof files in base64 format, mapped to the job's predeclared file fields.
statusstringNoAutomation data-dump mode only. One of "pending", "failed", or "declined". Defaults to "pending".
filesarrayNoAutomation data-dump mode only. Files to attach to the submission. Each file is written to disk and its public URL is embedded into the JSON job_proof under the key matching `name`. Each base64Data is capped at 5 MB.

Responses

StatusDescription
201Job task submitted successfullySuccess message indicating task submission, job task id and job id
400Bad request — e.g. job_proof is not a string, or in automation data-dump mode job_proof is not valid JSON / files validation failed.Error message describing the validation failure
403Forbidden. Returned for automation data-dump submissions when the caller is not a running agent (token must carry remote_device_id and job_task_id), the agent has no currently-running task, the running job and target job do not share the same automation_id, or the running job's owner has no access (owner / shared_view / shared_edit) to the target data-dump job.Error message describing the authorization failure
404Job not found, running task's job not found, or device not foundError message indicating the missing resource
GET/api/v2/planned_tasks

List unassigned planned tasks for a job

Returns planned tasks for a job that have not yet been assigned to a job task (i.e. job_task_id is null). Supports search, pagination, and sort order. Only the job owner (or a user the job is shared with) can list its planned tasks.

Query parameters

NameTypeRequiredDescription
job_idstringYesThe ID of the job whose planned tasks should be listed
searchstringNoCase-insensitive substring match against the planned task input (max 10,000 chars)
pageintegerNoPage number for pagination (starts from 1). Defaults to 1
limitintegerNoNumber of planned tasks to return per page (1-100). Defaults to 10
sortOrderstringNoSort order by creation time. One of "asc" or "desc". Defaults to "desc"

Responses

StatusDescription
200Planned tasks (unassigned) fetched successfully. Only tasks whose job_task_id is null are returned.{"success": true, "data": {"job": {...}, "plannedTasks": [{"planned_task_id": "...", "job_id": "...", "input": "...", "added": "..."}], "total": 42}}
400Bad request - Invalid query parameters{"success": false, "error": "Invalid job id"}
404Job not found or user doesn't own the job{"success": false, "error": "Job not found"}
500Internal server error{"success": false, "message": "Internal Server Error"}
POST/api/v2/planned_tasks/submit

Submit planned tasks for a job

This endpoint allows users to submit multiple planned tasks for a specific job. Planned tasks are pre-defined inputs that will be used when workers apply for the job.

Request body

NameTypeRequiredDescription
job_idstringYesThe ID of the job to submit planned tasks for
inputsarrayYesArray of input strings for planned tasks (usually JSON strings, minimum 1, maximum 10,000 characters per string)
remote_device_idstring (UUID v4)NoOptional device ID to pin all submitted tasks to a specific device. When set, the system will only assign these tasks to the specified device. If the device is not available, the task will be retried later.
device_namestringNoOptional device name (system-generated, two-word labels like "Atomic Mammal" or "Enormous Elephant") to pin all submitted tasks to a specific device. Resolved to a remote_device_id server-side. Ignored when remote_device_id is also provided. If the name matches more than one device, the request fails with 400 — use remote_device_id to disambiguate.
run_immediatelybooleanNoIf true, the system will attempt to assign the planned tasks immediately after creation (up to 3 attempts, 5 seconds apart). The response will include assignment_results indicating whether each task was assigned. Defaults to false.

Responses

StatusDescription
200Planned tasks submitted successfully. When run_immediately is true, includes assignment_results.{"success": true, "inserted_ids": [{"planned_task_id": "60c72b2f9b1e8c001c8e4d8a", "input": "{\"fname\":\"John\",\"email\":\"john@example.com\"}"}], "assignment_results": [{"planned_task_id": "60c72b2f9b1e8c001c8e4d8a", "assigned": true}]}
400Bad request - Invalid input data, exceeds available positions, or validation errors{"success": false, "error": "Invalid job id"} or {"success": false, "message": "No enough positions left. Received X tasks in excess."}
404Job not found or user doesn't own the job{"success": false, "error": "Job not found"}
500Internal server error{"success": false, "message": "Internal Server Error"}
DELETE/api/v2/tasks/delete

Delete a job task by ID

This endpoint allows employers to delete a job task. Accepts either a job_task_id or planned_task_id (a planned_task_id is resolved to its associated job task). Only tasks with certain statuses can be deleted to maintain data integrity (failed, declined, confirmed). The associated planned task is deleted along with the job task by default; pass keep_planned_task: true to instead preserve it (its job_task_id is reset to null) so it can be reused.

Request body

NameTypeRequiredDescription
task_idstringYesThe ID of the job task or planned task to delete. Accepts either a job_task_id or planned_task_id - if a planned_task_id is provided, it is resolved to its associated job task, which is then deleted.
keep_planned_taskbooleanNoIf true, the planned task associated with the deleted job task is preserved (its job_task_id is reset to null) so it can be reused; if false (default), that planned task is deleted along with the job task. Applies whether task_id is a job_task_id or a planned_task_id.

Responses

StatusDescription
200Job task deleted successfully. The associated planned task is deleted by default, or preserved with job_task_id set to null when keep_planned_task is true.{"success": true, "message": "Job task deleted successfully"}
400Bad request - Invalid task ID, task cannot be deleted due to status restrictions, or planned task exists but has no associated job task{"success": false, "error": "Invalid task id"} or {"success": false, "message": "Cannot delete job task with status 'confirmed'. Only tasks with status: failed, declined can be deleted."} or {"success": false, "error": "Planned task {id} exists but has no associated job task (job_task_id is null)"}
403Forbidden - User doesn't have permission to delete this job task{"success": false, "message": "You don't have permission to delete this job task"}
404Job task not found, or task_id is neither a valid job task ID nor a planned task ID{"success": false, "message": "Job task not found"} or {"success": false, "error": "task_id {id} is neither a valid job task ID nor a planned task ID"}
500Internal server error{"success": false, "message": "Internal Server Error"}
DELETE/api/v2/planned_tasks

Delete one or more planned tasks

Deletes unassigned planned tasks by their IDs. Planned tasks already assigned to a pending or successful job task cannot be deleted — reinitiate or delete the job task first. All IDs must belong to jobs owned by (or shared with edit permission to) the caller.

Request body

NameTypeRequiredDescription
planned_task_idsstring[]YesArray of planned task IDs to delete (minimum 1). A planned task that is already assigned to a pending or successful job task cannot be deleted.

Responses

StatusDescription
200Planned tasks deleted successfully{"success": true}
400Bad request - Invalid planned task id, or one of the planned tasks is already assigned to a pending/successful job task{"success": false, "message": "Cannot delete a planned task that is assigned to a pending / successful job task"}
404One or more planned tasks not found, or the job is not owned by the caller{"success": false, "message": "Planned task not found"}
500Internal server error{"success": false, "message": "Internal Server Error"}
POST/api/v2/planned_tasks/reinitiate

Re-initiate failed/declined tasks

This endpoint allows employers to re-initiate one or more failed or declined tasks. Accepts either a single task_id or an array of task_ids. This makes the planned tasks available again for workers to apply.

Request body

NameTypeRequiredDescription
task_idstringNoThe ID of a single job task to re-initiate (for backward compatibility)
task_idsstring[]NoAn array of job task IDs to re-initiate. Either task_id or task_ids must be provided.

Responses

StatusDescription
200Task re-initiated successfully{"success": true, "message": "Task successfully reinitiated"}
400Bad request - Invalid task ID or task cannot be reinitiated due to status restrictions{"success": false, "error": "Invalid task id"} or {"success": false, "message": "Cannot reinitiate task with status 'confirmed'. Only failed or declined tasks can be reinitiated."}
403Forbidden - User doesn't have permission to reinitiate this task{"success": false, "message": "You don't have permission to reinitiate this task"}
404Job task not found or no planned task associated with this job task{"success": false, "message": "Job task not found"}
500Internal server error{"success": false, "message": "Internal Server Error"}

Payments

Handle payment operations including withdrawals.

POST/api/v2/payments/withdraw

Withdraw user balance

Initiate a withdrawal to a wallet address.

Request body

NameTypeRequiredDescription
amountnumberYesAmount to withdraw
wallet_addressstringYesWallet address to receive the withdrawal

Responses

StatusDescription
200Withdrawal successful{"msg": "withdraw done"}
400Insufficient balance, below minimum payout, or invalid requestError message with specific reason
401Unauthorized or missing token
500Server error
GET/api/v2/payments/withdrawals

Get recent withdrawal details

Returns the list & details of recent withdrawals made by the user.

Query parameters

NameTypeRequiredDescription
pageintegerNoPage number for pagination (starts from 1)
limitintegerNoNumber of withdrawals to return per page

Responses

StatusDescription
200Details of the withdrawals made by the user
401Unauthorized or missing token
404No transactions found
500Server error
Example response
JSON
{
"total_count": 3,
"transactions": [
{
"_id": "67d9f0b57131be1625d301c4",
"reference_id": "26795145",
"user_id": "676d3619e618d4ad99dae899",
"recipient_id": null,
"crypto_uuid": null,
"payment_type": "Withdrawal",
"payment_method": "cryptomus",
"payment_details": "",
"transaction_fee": "0",
"currency": "USD",
"amount": "-10",
"wallet_address": "2w",
"status": "paid",
"reason": null,
"created": "2025-03-18T22:16:21.895Z",
"updated": "2025-03-18T22:16:21.895Z",
"process_date": "2025-03-18T22:16:21.895Z",
"transaction_id": "67d9f0b57131be1625d301c4",
"transaction_proof": "2ca560b36dcdd7c8",
"__v": 0
}
],
"page": 1,
"balance": 3.54,
"total_withdraw": -30,
"total_deposit": 0
}

User

User account management and balance information.

GET/api/v2/user/balance

Get user balance

Returns the user's current balance, active balance and pending earnings.

Responses

StatusDescription
200User balance information retrieved successfully
401Unauthorized or missing token
500Server error
Example response
JSON
{
"balance": 4.434,
"active_balance": 4.434,
"pending_earnings": 1.25
}

Devices

Device management and information retrieval endpoints.

GET/api/v2/devices

Get debuggable devices

Retrieves a list of devices that are either owned by the user or rented with live payment type.

Responses

StatusDescription
200Successfully retrieved devices list
401Unauthorized or missing token
500Server error
Example response
JSON
[
{
"remote_device_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"added": "2025-08-19T10:00:00Z",
"automations": [],
"brand": "Samsung",
"country": "US",
"isEmulator": false,
"model": "Galaxy S21",
"name": "Atomic Mammal",
"networkType": "wifi",
"numberOfCores": 8,
"online": true,
"processor": "Snapdragon 888",
"ramMb": 8192,
"sdkVersion": "31",
"version": "12"
}
]
GET/api/v2/devices/market

List market devices (action / online)

Returns devices that are listed on the market with payment_type 'action' and currently online. Each item includes the device name, country, and an isAvailable flag (false when the device has at least one job task in 'running' status, true otherwise). Results can be filtered by country and are sorted by country.

Query parameters

NameTypeRequiredDescription
countrystringNoFilter devices by country.
sortDirectionstringNoSort direction for the country field. "asc" or "desc". Defaults to "asc".

Responses

StatusDescription
200Successfully retrieved the list of online, action-payment market devices.
401Unauthorized or missing token
500Server error
Example response
JSON
[
{
"name": "Atomic Mammal",
"country": "BR",
"isAvailable": true
},
{
"name": "Enormous Elephant",
"country": "US",
"isAvailable": false
}
]
GET/api/v2/devices/verified-phone-number

Get the device's verified phone number

Agent-scoped endpoint. Identifies the device from the automation agent token and returns the phone number stored on the device record only when phoneNumberVerified is true. Returns null otherwise. Backs `agent.info.getVerifiedPhoneNumber` in the automation bootstrap.

Responses

StatusDescription
200Returns the verified phone number stored on the device record, or null when the device has no verified phone number.
400Token is not bound to a device (e.g. user-scoped token rather than an automation agent token).
401Unauthorized or missing token
404Device not found
500Server error
Example response
JSON
{
"phoneNumber": "+14155550123"
}

Proxy

Manage SOCKS5/HTTP proxy credentials. A connection is one credential pair (`connectionId` + `password`) routed through one of your devices. Re-pointing the association to a different device is the IP-rotation primitive — new sessions begin exiting through the new device within ~30 s (orchestrator auth-cache TTL); in-flight sessions stay on the old device until they close. Endpoints are scoped to devices the caller owns; the admin endpoint requires the admin token.

POST/api/v2/devices/connections/list

List my connections

All connections owned by, or currently routed through, devices the caller owns. The `password` is plaintext — RMS owns the credential.

Responses

StatusDescription
200All connections owned by, or routed through, devices the caller owns.
401Missing or invalid API token.
Example response
JSON
{
"success": true,
"connections": [
{
"connectionId": "wXyZ04827193",
"password": "p9F7bX3cVqLt",
"ownerUserId": "add02036-6754-4cf7-8432-cc28be4468f0",
"name": "client-A",
"createdAt": "2026-04-25T13:22:11Z",
"deviceId": "add02036-6754-4cf7-8432-cc28be4468f0"
}
]
}
POST/api/v2/devices/connections/create

Create a connection

Generates a fresh `connectionId` (4 letters + 8 digits) and 12-char password, associates it with the chosen device, and syncs to the orchestrator + legacy mirror. The plaintext password is in the response — save it now.

Request body

NameTypeRequiredDescription
device_idstring (UUID — `remote_device_id`)YesOwned device id.
namestringNoOptional human label (≤ 100 chars). Cosmetic only — the credential is the auto-generated `connectionId`.

Responses

StatusDescription
200Created — RMS generates a `connectionId` (4 letters + 8 digits) + 12-char password and pushes to the orchestrator. Save the plaintext password from the response: the orchestrator stores only a hash.
400`device_id` missing.
404Device not owned by caller.
Example response
JSON
{
"success": true,
"connection": {
"connectionId": "wXyZ04827193",
"password": "p9F7bX3cVqLt",
"ownerUserId": "add02036-6754-4cf7-8432-cc28be4468f0",
"name": "client-A",
"createdAt": "2026-04-25T13:22:11Z",
"deviceId": "add02036-6754-4cf7-8432-cc28be4468f0"
}
}
POST/api/v2/devices/connections/delete

Delete a connection

Cascades on RMS, orchestrator, and the legacy mirror. Active sessions are terminated.

Request body

NameTypeRequiredDescription
connection_idstringYesThe connection's id — a 12-char token (4 letters + 8 digits), also the SOCKS5 username.

Responses

StatusDescription
200Deleted on RMS, the orchestrator, and the legacy mirror. Active proxy sessions using this credential are terminated.
403Connection not owned/routed through any device the caller owns.
404Connection not found.
POST/api/v2/devices/connections/password/rotate

Rotate password

Replaces the connection's password (same `connectionId`). Active sessions continue on the old credential until they close.

Request body

NameTypeRequiredDescription
connection_idstringYesThe connection's id — a 12-char token (4 letters + 8 digits), also the SOCKS5 username.

Responses

StatusDescription
200Generates a new 12-char password (same `connectionId`).
403Forbidden.
Example response
JSON
{ "success": true, "password": "qK4LmPzWxR7a" }
POST/api/v2/devices/connections/association/set

Move connection to another device (IP rotation)

Hot-swap the exit device. Owner must own both the source connection and the target device.

Request body

NameTypeRequiredDescription
connection_idstringYesThe connection's id — a 12-char token (4 letters + 8 digits), also the SOCKS5 username.
device_idstring (UUID)YesTarget device — **must also be owned by the caller**.

Responses

StatusDescription
200Hot-swap successful. New SOCKS5/HTTP CONNECT sessions begin routing to the new device within ~30 s (orchestrator auth-cache TTL). In-flight sessions stay on the old device until they close.
403Source connection or target device not owned by caller.
POST/api/v2/devices/connections/association/delete

Remove association

Connection stays alive but cannot authenticate until re-associated.

Request body

NameTypeRequiredDescription
connection_idstringYesThe connection's id — a 12-char token (4 letters + 8 digits), also the SOCKS5 username.

Responses

StatusDescription
200Connection stays but cannot route until a new association is set. Active sessions are terminated.
403Forbidden.
POST/api/v2/devices/connections/blacklist/list

List per-connection blacklist

Per-connection block list (in addition to global + per-device).

Request body

NameTypeRequiredDescription
connection_idstringYesThe connection's id — a 12-char token (4 letters + 8 digits), also the SOCKS5 username.

Responses

StatusDescription
200Per-connection blacklist domains (in addition to the global blacklist). Suffix matching applies.
403Forbidden.
Example response
JSON
{ "success": true, "domains": ["example.com", "tracker.io"] }
POST/api/v2/devices/connections/blacklist/add

Add domains to blacklist

Adds one or more domains to the connection's blacklist. Suffix matching applies on the proxy side — blocking `example.com` also blocks `sub.example.com`.

Request body

NameTypeRequiredDescription
connection_idstringYesThe connection's id — a 12-char token (4 letters + 8 digits), also the SOCKS5 username.
domainsstring[]YesNon-empty array of domains to add or remove.

Responses

StatusDescription
200Updated.
403Forbidden.
POST/api/v2/devices/connections/blacklist/remove

Remove domains from blacklist

Removes one or more domains from the connection's blacklist.

Request body

NameTypeRequiredDescription
connection_idstringYesThe connection's id — a 12-char token (4 letters + 8 digits), also the SOCKS5 username.
domainsstring[]YesNon-empty array of domains to add or remove.

Responses

StatusDescription
200Updated.
403Forbidden.
POST/api/v2/devices/connections/whitelist/list

List whitelisted IPs

Returns the IP/CIDR patterns currently on the connection's whitelist. When a customer's source IP matches any pattern, they can open proxy streams without a password — just the `connectionId` in the SOCKS5/HTTP username field.

Request body

NameTypeRequiredDescription
connection_idstringYesThe connection's id — a 12-char token (4 letters + 8 digits), also the SOCKS5 username.

Responses

StatusDescription
200Array of whitelisted IP patterns.
401Missing or invalid API token.
Example response
JSON
{
"success": true,
"ips": ["203.0.113.5", "10.0.0.0/8"]
}
POST/api/v2/devices/connections/whitelist/add

Add IP to whitelist

Adds an IP or CIDR to the connection's whitelist. Idempotent — re-posting the same pattern updates its label. Domain patterns are rejected (use blacklist for domain-level control).

Request body

NameTypeRequiredDescription
connection_idstringYesThe connection's id — a 12-char token (4 letters + 8 digits), also the SOCKS5 username.
patternstringYesIP address or CIDR to whitelist (e.g. `1.2.3.4` or `10.0.0.0/8`). Domain patterns are rejected.
labelstringNoOptional human label (e.g. `office`, `CI runner`).

Responses

StatusDescription
200Whitelist entry added/removed.
400Invalid pattern (must be IP or CIDR).
401Missing or invalid API token.
502Gost orchestrator unreachable.
POST/api/v2/devices/connections/whitelist/remove

Remove IP from whitelist

Removes an IP/CIDR from the connection's whitelist.

Request body

NameTypeRequiredDescription
connection_idstringYesThe connection's id — a 12-char token (4 letters + 8 digits), also the SOCKS5 username.
patternstringYesIP or CIDR to remove from the whitelist.

Responses

StatusDescription
200Whitelist entry added/removed.
400Invalid pattern (must be IP or CIDR).
401Missing or invalid API token.
502Gost orchestrator unreachable.
POST/api/v2/devices/transport-mode/get

Get device transport mode

Returns the current egress transport mode for the device. Determines which network interface outbound traffic exits through.

Request body

NameTypeRequiredDescription
device_idstring (UUID — `remote_device_id`)YesOwned device id.

Responses

StatusDescription
200Mode returned or updated.
400Invalid mode value.
401Missing or invalid API token.
403Device not owned by caller.
502Gost orchestrator unreachable.
POST/api/v2/devices/transport-mode/set

Set device transport mode

Changes the egress transport mode. The phone picks up the change within one heartbeat interval (~30 s). `cellular` uses the carrier IP (default), `wifi` uses the WiFi router's public IP, `auto` prefers cellular with WiFi fallback. Note: `wifi` mode is partial — the downstream transport binding (cellular + WiFi tunnel for cost savings) requires additional phone-side work.

Request body

NameTypeRequiredDescription
device_idstring (UUID — `remote_device_id`)YesOwned device id.
modestringYes`cellular` (default — carrier IP), `wifi` (WiFi router's IP), or `auto` (prefer cellular, fallback to WiFi).

Responses

StatusDescription
200Mode returned or updated.
400Invalid mode value.
401Missing or invalid API token.
403Device not owned by caller.
502Gost orchestrator unreachable.
POST/api/v2/devices/proxy-logs/list

Query request logs

One log entry per SOCKS5 / HTTP CONNECT attempt. Backed by ClickHouse; results are post-filtered to devices owned by the caller.

Request body

NameTypeRequiredDescription
fromstring (ISO 8601)YesInclusive lower bound on `timestamp`.
tostring (ISO 8601)YesInclusive upper bound on `timestamp`.
connectionIdstringNoFilter to one connection.
deviceIdstring (UUID)NoFilter to one device — must be owned by the caller.
targetstringNoSubstring match on the request target (`host:port`).
action"allowed" | "blocked"NoFilter by outcome.
limitintegerNoPage size (default 100, max 10000).
offsetintegerNo0-indexed offset for pagination.
backend"zarif" | "gost"NoWhich proxy orchestrator to query. Default `zarif`. Pass in the JSON request body for POST endpoints; URL query params are ignored.

Responses

StatusDescription
200Request log rows from ClickHouse. Post-filtered to devices owned by the caller.
400`from`/`to` missing or invalid; `limit` out of range.
Example response
JSON
{
"success": true,
"total": 12345,
"entries": [
{
"timestamp": "2026-04-25T13:22:11Z",
"connectionId": "wXyZ04827193",
"deviceId": "add02036-6754-4cf7-8432-cc28be4468f0",
"serverNode": "server-us-aws",
"target": "example.com:443",
"targetType": "domain",
"port": 443,
"resolvedIp": "",
"action": "allowed",
"blockReason": null,
"bytesIn": 184213,
"bytesOut": 4218
}
]
}
POST/api/v2/devices/proxy-bandwidth

Query bandwidth aggregates

Per-(device, connection) byte counts, optionally grouped by hour or day. When `deviceId` is omitted the backend issues one query per owned device and merges.

Request body

NameTypeRequiredDescription
deviceIdstring (UUID)NoFilter to one device — must be owned by the caller. Omit to fetch per-device rows for every device the caller owns.
connectionIdstringNoFilter to one connection.
fromstring (ISO 8601)NoInclusive lower bound.
tostring (ISO 8601)NoInclusive upper bound.
groupBy"hour" | "day"NoTime-bucket granularity.
backend"zarif" | "gost"NoWhich proxy orchestrator to query. Default `zarif`. Pass in the JSON request body for POST endpoints; URL query params are ignored.

Responses

StatusDescription
200Aggregated bandwidth rows from the orchestrator.
Example response
JSON
{
"success": true,
"rows": [
{
"deviceId": "add02036-6754-4cf7-8432-cc28be4468f0",
"connectionId": "wXyZ04827193",
"bytesIn": 152841234,
"bytesOut": 9183748,
"bucket": "2026-04-25T13:00:00Z"
}
]
}
GET/api/v2/admin/devices/proxies

List all proxy connections (admin)

Returns every proxy connection across all users, joined with the associated device's metadata and the orchestrator's bandwidth aggregates (24h + lifetime). Requires the admin API token (`X-Admin-Token`). `device` is `null` when the connection has no current association.

Query parameters

NameTypeRequiredDescription
backend"zarif" | "gost"NoWhich proxy orchestrator to read aggregates from (URL query string, since this endpoint is GET). Default `zarif`.

Responses

StatusDescription
200Connection list with associated device + bandwidth.
400Unknown `backend` value.
401Missing or invalid admin API token.
502Could not reach the proxy orchestrator.
503Proxy orchestrator / RMS not configured (env vars missing).
Example response
JSON
{
"total": 2,
"proxies": [
{
"connection_id": "wXyZ04827193",
"name": "client-A",
"password": "p9F7bX3cVqLt",
"created_at": "2026-04-25T13:22:11Z",
"bytes_last_24h": 152841234,
"bytes_lifetime": 9183748192,
"device": {
"remote_device_id": "add02036-6754-4cf7-8432-cc28be4468f0",
"user_id": "65f3a1c0d4e2f10012abcd34",
"name": "Atomic Mammal",
"brand": "Samsung",
"model": "SM-G998B",
"country": "US",
"online": true,
"is_rented": false,
"is_onMarket": false
}
},
{
"connection_id": "aBcD19384756",
"name": null,
"password": "qK4LmPzWxR7a",
"created_at": "2026-04-25T13:22:12Z",
"bytes_last_24h": 0,
"bytes_lifetime": 24576,
"device": null
}
]
}

Bucket

Per-(job, device) JSON bucket — the persistent state automations attach to a single hire on a single device. The endpoints below are the **employer-facing** surface, gated by job ownership / share permission. They mirror the same model as the **Buckets** tab on the Job details page. (The agent surface used from inside automations is `agent.utils.bucket` — see the automation docs.)

GET/api/v2/bucket/:job_id

List buckets for a job

Returns a paginated list of every bucket attached to this job, with the device display name resolved for each.

Path parameters

NameTypeRequiredDescription
job_idstring (ObjectId)YesPath param. The job's `job_id`. Caller must own the job, or have it shared to them (`view` for reads, `edit` for writes/deletes).

Query parameters

NameTypeRequiredDescription
pageintegerNo1-indexed page. Defaults to `1`.
limitintegerNoPage size. Defaults to `20`. Capped at `100`.

Responses

StatusDescription
200Paginated list of buckets for this job (newest first by `updated_at`). Each entry is enriched with the device's display name.
404Job not found, or caller does not own / does not have the required share permission (`view` for reads, `edit` for writes / deletes).{ "error": "Job not found" }
500Unexpected DB error.{ "error": "Internal server error" }
Example response
JSON
{
"buckets": [
{
"_id": "65f1...",
"job_id": "65aa...",
"remote_device_id": "8b2f-uuid",
"data": { "sessionToken": "abc123", "loggedIn": true },
"updated_at": "2026-04-15T08:14:00.000Z",
"device_name": "Pixel 5 — Lab"
}
],
"total": 1,
"page": 1,
"pages": 1
}
DELETE/api/v2/bucket/:job_id

Delete all buckets for a job

Wipes every bucket associated with this job. Caller must have `edit` access (owner or shared-edit).

Path parameters

NameTypeRequiredDescription
job_idstring (ObjectId)YesPath param. The job's `job_id`. Caller must own the job, or have it shared to them (`view` for reads, `edit` for writes/deletes).

Responses

StatusDescription
200All buckets for this job removed. Returns the count deleted.{ "success": true, "deleted": 12 }
404Job not found, or caller does not own / does not have the required share permission (`view` for reads, `edit` for writes / deletes).{ "error": "Job not found" }
500Unexpected DB error.{ "error": "Internal server error" }
GET/api/v2/bucket/:job_id/:device_id

Read a single bucket

Returns the bucket for this `(job_id, device_id)` pair. 404 when none exists or the job is inaccessible.

Path parameters

NameTypeRequiredDescription
job_idstring (ObjectId)YesPath param. The job's `job_id`. Caller must own the job, or have it shared to them (`view` for reads, `edit` for writes/deletes).
device_idstring (UUID)YesPath param. Device's `remote_device_id`.

Responses

StatusDescription
200Returns the bucket document for this `(job_id, device_id)` pair.
404Either the job is inaccessible to the caller, or no bucket exists for this device.{ "error": "Bucket not found" }
500Unexpected DB error.{ "error": "Internal server error" }
Example response
JSON
{
"bucket": {
"_id": "65f1...",
"job_id": "65aa...",
"remote_device_id": "8b2f-uuid",
"data": { "sessionToken": "abc123", "loggedIn": true },
"updated_at": "2026-04-15T08:14:00.000Z"
}
}
PUT/api/v2/bucket/:job_id/:device_id

Write a bucket

Replaces the bucket payload for this `(job_id, device_id)` pair (no merge — `data` becomes the new bucket). Validated against the automation's `bucket_schema` when one is defined. Caller must have `edit` access.

Path parameters

NameTypeRequiredDescription
job_idstring (ObjectId)YesPath param. The job's `job_id`. Caller must own the job, or have it shared to them (`view` for reads, `edit` for writes/deletes).
device_idstring (UUID)YesPath param. Device's `remote_device_id`.

Request body

NameTypeRequiredDescription
dataobjectYesNew bucket payload. Replaces the existing object (no merge). If the linked automation defines a `bucket_schema`, `data` is validated against it.

Responses

StatusDescription
200Bucket created or replaced for this `(job_id, device_id)` pair. Returns the saved document.
400`data` missing / not an object, or schema validation against the automation's `bucket_schema` failed.{ "error": "Invalid bucket data" }
404Job not found, or caller does not own / does not have the required share permission (`view` for reads, `edit` for writes / deletes).{ "error": "Job not found" }
500Unexpected DB error.{ "error": "Internal server error" }
DELETE/api/v2/bucket/:job_id/:device_id

Delete a bucket

Removes the bucket for this `(job_id, device_id)` pair. Caller must have `edit` access.

Path parameters

NameTypeRequiredDescription
job_idstring (ObjectId)YesPath param. The job's `job_id`. Caller must own the job, or have it shared to them (`view` for reads, `edit` for writes/deletes).
device_idstring (UUID)YesPath param. Device's `remote_device_id`.

Responses

StatusDescription
200Bucket for this `(job_id, device_id)` pair removed.{ "success": true }
404Job not found, or caller does not own / does not have the required share permission (`view` for reads, `edit` for writes / deletes).{ "error": "Job not found" }
500Unexpected DB error.{ "error": "Internal server error" }

Device Bucket

Per-device JSON bucket, shared across every job and automation running on the device. Useful for cross-job state — login cookies, account tokens, app config. Distinct from the per-job bucket: there is no `bucket_schema` validation, and no job context is required, so direct-run automations can use it too. The agent calls these via `agent.utils.deviceBucket`.

POST/api/v2/device-bucket

Read device bucket

Reads the device-scoped bucket. The `remote_device_id` is taken from the token when it carries a device context; non-agent callers must supply it in the body.

Request body

NameTypeRequiredDescription
remote_device_idstring (UUID)NoThe device's `remote_device_id`. Optional when the token already carries a device context (agent tokens). Required for plain Client API tokens. Must point to a device the caller owns or is currently running on.

Responses

StatusDescription
200Returns the device-scoped bucket. `deviceBucket` is `{}` on first read.
400Token has no device context and `remote_device_id` was not supplied.{ "error": "Device bucket requires a device context (remote_device_id)" }
Example response
JSON
{
"deviceBucket": {
"accountToken": "xyz",
"username": "user@example.com"
}
}
POST/api/v2/device-bucket/set

Merge into device bucket

Merges the supplied object into the existing device bucket. Existing keys not in the payload are preserved.

Request body

NameTypeRequiredDescription
remote_device_idstring (UUID)NoThe device's `remote_device_id`. Optional when the token already carries a device context (agent tokens). Required for plain Client API tokens. Must point to a device the caller owns or is currently running on.
dataobjectYesNon-null, non-array JSON object. Top-level keys are merged with the stored bucket — existing keys not in the payload are preserved.

Responses

StatusDescription
200Bucket merged and persisted. Returns the full merged object.
400Missing device context or `data` is missing / null / an array / not an object.{ "error": "Invalid data. Must be a non-null object." }
Example response
JSON
{
"success": true,
"deviceBucket": {
"accountToken": "xyz",
"username": "user@example.com"
}
}

Other

File upload and temporary file access endpoints.

POST/api/v2/files/upload

Upload temporary file

Uploads a file to temporary storage. Files are automatically deleted after 15 minutes. Maximum file size is 50 MB. Use multipart/form-data with 'file' field name.

Request body

NameTypeRequiredDescription
filefileYesFile to upload (multipart/form-data)

Responses

StatusDescription
200File uploaded successfully{ "success": true, "message": "File uploaded successfully", "data": { "filename": "1234567890_example.pdf", "originalName": "example.pdf", "size": 1024000, "url": "https://api.example.com/temp/1234567890_example.pdf", "expiresAt": "2025-10-08T12:15:00.000Z" } }
400No file uploaded{ "success": false, "message": "No file uploaded" }
401Unauthorized or missing tokenAuthentication error message
500Server error during upload{ "success": false, "message": "Error uploading file", "error": "Error details" }
Example response
JSON
{
"success": true,
"message": "File uploaded successfully",
"data": {
"filename": "1234567890_example.pdf",
"originalName": "example.pdf",
"size": 1024000,
"url": "https://api.example.com/temp/1234567890_example.pdf",
"expiresAt": "2025-10-08T12:15:00.000Z"
}
}
GET/api/v2/files/temp/:filename

Get uploaded file

Retrieves a previously uploaded temporary file. Files expire after 15 minutes from upload time.

Path parameters

NameTypeRequiredDescription
filenamestringYesFilename returned from the upload endpoint (path parameter)

Responses

StatusDescription
200File retrieved successfullyBinary file data
400Filename is required{ "success": false, "message": "Filename is required" }
404File not found or expired{ "success": false, "message": "File not found or expired" }
401Unauthorized or missing tokenAuthentication error message
500Server error during retrieval{ "success": false, "message": "Error retrieving file", "error": "Error details" }