Track
This page explains scheduled web tasks, check records, and the compatibility boundary between the current backend and the Node SDK.
Version and deployment boundary
npm @qoniai/qoni@0.9.0 uses the legacy /track/monitors route, which returned 404 in the 2026-10-08 gray acceptance test and cannot be used with the current /track/tracks backend. HTTP below describes that backend contract; Node examples describe an unpublished repair candidate (Unreleased, targeting 0.10.0), not released 0.9.0 functionality. Check your deployment's OpenAPI and SDK release notes before use. This is a breaking change and must not be described as available in npm 0.9.x. Downloads remain pinned to official 0.9.0 and do not contain the candidate.
What it solves
Track persists a schedule and repeats web checks through a DoAnything session. Use it for vendor pricing, policy, or partner portal patrols. Put target URLs, extraction requirements, and change criteria in natural-language instructions; your application must still verify the output against business requirements.
Core capabilities
The current backend supports scheduled creation, definition reads, pause/resume, immediate execution, recent check review, and soft deletion. Scheduling and task-reference boundaries are explained below.
Typical scenarios
- Vendor monitor agent: repeat pricing and policy checks, with application verification of sources and changes.
- Partner portal monitoring agent: signed-in watching remains a design requiring deployment support; do not pass the old
profile_id.
When to use it
Use DoAnything for one task and WebSearch for public search. Current Track has no legacy extraction DSL, trigger DSL, notification channel, or alignment questions, and does not guarantee a notification per check.
Create a Track
POST /api/v1/projects/{pid}/track/tracks
Authorization: Bearer wa_...
Content-Type: application/json| HTTP field | Description |
|---|---|
instructions | Required, nonempty task instructions, up to 4000 characters; include target URLs and check requirements here |
title | Optional title, up to 120 characters |
schedule | Required; neither inferred from instructions nor supplied by an SDK default |
Choose one schedule:
- Interval:
{"every_seconds":3600}, integer seconds from 600 to 30 days (2592000 seconds). - Daily:
{"daily_at":"09:00","tz":"Asia/Singapore"}, 24-hourHH:MMand a time zone; the backend starts 0–30 minutes before that time, not necessarily at 09:00.
Arbitrary cron, target_urls, profile_id, extraction_schema, trigger_dsl, stop_condition_dsl, tick_instructions, and notify_channel are unsupported. Legacy fields cannot be copied to the new resource.
Node SDK
The example below applies only to the unpublished repair candidate.
High-level prompt maps verbatim to HTTP instructions. The historical handle name MonitorHandle remains, representing the new Track resource.
// Unpublished repair candidate; npm 0.9.0 does not support this backend contract.
const monitor = await qoni.track.create({
token,
prompt: "Check the title of https://example.com/ for changes; return the title and source URL.",
title: "Example page patrol",
schedule: { kind: "daily", at: "09:00", tz: "Asia/Singapore" },
});
const current = await monitor.get();
const recent = await monitor.runs({ limit: 10, offset: 0 });
console.log(recent);Creation immediately starts the first check (kind: first_look). The example only reads the definition and checks; it does not call runNow() immediately. checks reports running for an in-flight check and done for successful completion; other terminal states are failed/expired/canceled. This differs from the unified RunResult.status value succeeded.
Before a manual execution, wait for the first check's exact run ID to reach a terminal state and verify that no check is in flight. A race can still occur between that read and runNow(): on HTTP 409 task_in_progress, retain the existing check, keep reading its status, and let the user explicitly request a later manual execution. Do not retry automatically or count 409 as success.
For an interval use schedule: { kind: "interval", intervalSeconds: 3600 }. The candidate validates schedule bounds locally and rejects cron, legacy DSL, targetUrls, profileId, notifications, and other unsupported fields with QoniUnsupportedError, without silently ignoring or converting them.
| Method | Candidate behavior and boundary |
|---|---|
get() | Read the Track definition |
pause() / resume() | Set status: paused/active; pausing does not cancel a running task |
refine(patch) | Change only title/schedule, preserving instructions; create a new Track for a new intent |
runNow() | Return a real trackId/sessionId/runId task reference; returns 409 task_in_progress while first_look or another check is in flight; HTTP 202 means accepted, so query the check's terminal state |
runs({ limit?, offset? }) | Read the most recent 50 checks, newest first; parameters paginate locally within that window, not all history |
run(runId) | Find the exact run ID in the recent 50 checks; fail if absent, without falling back to arbitrary DoAnything task reads |
delete() | Soft-delete and stop future scheduling; do not stop the current run |
There is no current Track SSE or question/intervention endpoint. Candidate events() (on iteration), interactionHandle(), track.api.events(), and track.api.intervene() throw QoniUnsupportedError locally with zero HTTP. Track scopes cannot authorize DoAnything interactions; obtain separate DoAnything authorization and handle the corresponding task.
On the first get() readback, capture current.lastTaskId (HTTP last_task_id) once as the original first-look ID. checking says whether the latest check is still in flight (backend pending/running/waiting are exposed as running). checking: false alone does not prove business success: read the original ID's terminal status and output in checks. Do not replace the captured first-look ID after pause/resume or a later readback.
Check records
HTTP checks.items contains only the latest 50 records. SDK runs() converts field names to camelCase while preserving status/outcome values:
| HTTP field (SDK field) | Output and boundary |
|---|---|
run_id (runId) | The exact task ID for this check; retain it for subsequent reads |
kind | first_look, scheduled, or manual |
status | In flight: running; terminal: done/failed/expired/canceled |
outcome | changed, same, or needs_you; may be null without a report. needs_you does not provide a Track question-reply API |
headline | Report summary; older records may fall back to the answer's first line; may be null |
answer | Check answer, possibly null; verify its content and sources |
failure | Failure details for failed/expired, otherwise null; empty failure details do not imply success |
created_at/ended_at (createdAt/endedAt) | Creation/end timestamps; ended_at is null while unfinished |
step | Latest action label for an in-flight check; may be null without a record |
Failures and automatic pause
| Response/condition | Handling |
|---|---|
Create: 409 track_limit | At most 20 retained Tracks per project/user, including paused Tracks; delete an unneeded Track to release capacity |
Execute: 409 task_in_progress | The original task is still in flight; read that ID without automatic retries |
422 invalid_track | Service validation found an invalid schedule or task definition; fix the input. Request-model field/type validation may also return 422 |
404 track_not_found | The Track is absent, soft-deleted, or outside the caller's visible scope; do not substitute another resource ID |
3 consecutive checks without a track_report | The scheduler automatically pauses on a subsequent scheduling check; read the state and inspect the conversation/failures before deciding to resume |
A successful report resets the no-report counter. done does not replace report and business-output verification. A missing run(runId) record in the recent 50 window is the candidate SDK's local not_found with no HTTP status; distinguish it from backend track_not_found.
REST lifecycle
GET /api/v1/projects/{pid}/track/tracks
GET /api/v1/projects/{pid}/track/tracks/{track_id}
PATCH /api/v1/projects/{pid}/track/tracks/{track_id}
DELETE /api/v1/projects/{pid}/track/tracks/{track_id}
POST /api/v1/projects/{pid}/track/tracks/{track_id}/run_now
GET /api/v1/projects/{pid}/track/tracks/{track_id}/checksPATCH accepts resource fields directly, for example {"status":"paused"}, without the old action/patch wrapper. HTTP can update instructions/title/schedule/status; the SDK's high-level refine() exposes only title/schedule to preserve intent. checks returns items, at most the recent 50 records; there is no legacy /runs/{run_id} full-history detail endpoint.
Checkpoint and next step
Read back the definition and schedule after creation. After runNow(), use the exact returned run ID to query checks, wait for a terminal state (done for success), then verify the output and sources. A missing check record or a 202 response alone does not prove success. After deleting the Track, still reconcile the current task's status.
Until the candidate is released, changing a parameter cannot fix the legacy route in published 0.9.0. See Qoni SDK for file artifacts and Profiles for login-state reuse boundaries.