36 MCP tools for projects, tasks, workers, QR/NFC tags, and AI remediation. Task. Scan. Done.
Maintain this server? Claim it
Call this server's tools from your browser. Nothing to install.
Requests go through mcptry because browsers can't call most MCP servers directly. Header values are sent to the server for that request only and are never stored or logged.
Tool list from our last check. Connect live to refresh it.
Add one or more subtasks to a task (bulk). Subtasks support typed responses: "number" for per-set data (reps, weight, distance), "text" for notes, "choice" for options, "checkbox" for simple steps. Set-logging example: task "Bench Press" with subtasks Set 1/Set 2/Set 3 each response_type "number" — each completed set stores its value and timestamp, giving per-set timing for progression tracking.
Parent task ID
Array of subtasks to create
This tool isn't marked read-only, so it may change something on the server or in an account you connected.
Tool list last changed Oct 25, 2026.
Add one or more subtasks to a task (bulk). Subtasks support typed responses: "number" for per-set data (reps, weight, distance), "text" for notes, "choice" for options, "checkbox" for simple steps. Set-logging example: task "Bench Press" with subtasks Set 1/Set 2/Set 3 each response_type "number" — each completed set stores its value and timestamp, giving per-set timing for progression tracking.
| Argument | Type | Description |
|---|---|---|
| task_id* | string | Parent task ID |
| subtasks* | array | Array of subtasks to create |
{
"type": "object",
"required": [
"task_id",
"subtasks"
],
"properties": {
"task_id": {
"type": "string",
"description": "Parent task ID"
},
"subtasks": {
"type": "array",
"items": {
"type": "object",
"required": [
"title"
],
"properties": {
"title": {
"type": "string",
"description": "Subtask title (e.g. \"Set 1\")"
},
"sort_order": {
"type": "number",
"description": "Explicit position (defaults to end, in array order)"
},
"description": {
"type": "string",
"description": "Optional detail"
},
"response_type": {
"enum": [
"checkbox",
"number",
"text",
"choice",
"link",
"url",
"file"
],
"type": "string",
"description": "Default checkbox. \"number\" for per-set values. \"choice\" needs response_config.options. \"link\" = the worker submits a URL. \"url\" = the worker visits a preset URL and confirms. \"file\" = the worker uploads a file."
},
"requires_photo": {
"type": "boolean",
"description": "Require photo on completion"
},
"response_config": {
"type": "object",
"description": "For \"choice\": {options: [...]}. For \"number\"/\"text\": {placeholder}."
}
}
},
"description": "Array of subtasks to create"
}
}
}Add one or more tasks to an event (task list). Supports bulk creation. IMPORTANT: Set response_type correctly — use "text" for info collection (names, phones, emails, notes), "photo" for visual verification (inspections, serial numbers, damage checks), "checkbox" only for simple confirmations. NOTE: To dispatch tasks to an AI agent use tascan_dispatch_to_agent instead. Writing into an agent inbox list requires the agent:dispatch permission (agent:dispatch:code for CODE:/SHELL: titles) — without it the call is refused.
| Argument | Type | Description |
|---|---|---|
| tasks* | array | Array of tasks to create |
| list_id* | string | Task list (event) ID |
| idempotency_key | string | B1: optional replay key (up to 1024 chars, no braces) sent verbatim as the Idempotency-Key header instead of a fresh one per call — a retried call with the SAME key replays the original response instead of creating the tasks twice. |
{
"type": "object",
"required": [
"list_id",
"tasks"
],
"properties": {
"tasks": {
"type": "array",
"items": {
"type": "object",
"required": [
"title"
],
"properties": {
"title": {
"type": "string",
"description": "Task title"
},
"subtasks": {
"type": "array",
"items": {
"type": "object",
"required": [
"title"
],
"properties": {
"title": {
"type": "string",
"description": "Subtask title (e.g. \"Set 1\")"
},
"response_type": {
"enum": [
"checkbox",
"number",
"text",
"choice",
"link",
"url",
"file"
],
"type": "string",
"description": "Typed subtask response. \"number\" for per-set values (reps/weight), \"text\" for notes, \"choice\" needs response_config.options. \"link\" = the worker submits a URL. \"url\" = the worker visits a preset URL and confirms. \"file\" = the worker uploads a file. Default checkbox."
},
"requires_photo": {
"type": "boolean"
},
"response_config": {
"type": "object",
"description": "Same shape as task response_config"
}
}
},
"description": "Subtasks created with the task. Example set logging: [{title:\"Set 1\",response_type:\"number\"},{title:\"Set 2\",response_type:\"number\"},{title:\"Set 3\",response_type:\"number\"}]"
},
"sort_order": {
"type": "number",
"description": "Explicit sort position (defaults to end of list, in array order)"
},
"assigned_to": {
"type": "string",
"description": "Worker to assign this task to — pass the worker UUID (validated against your org and stored as the worker's name, which is how the worker portal matches \"my tasks\"). A plain name string is also accepted as-is."
},
"description": {
"type": "string",
"description": "Task description"
},
"response_type": {
"enum": [
"checkbox",
"photo",
"text",
"number",
"date",
"choice",
"link",
"url",
"file",
"signature"
],
"type": "string",
"description": "CRITICAL: \"text\" for names, phones, emails, notes, addresses, any free-form input. \"photo\" for tasks needing photographic proof (inspections, serial numbers, packed cases). \"checkbox\" ONLY for simple yes/no confirmations. \"number\" for numeric values. \"date\" for dates. \"choice\" for multiple-choice (needs response_config.options). \"link\" = the worker SUBMITS a URL (auto-verified by an http probe). \"url\" = the worker VISITS a preset URL and confirms. \"file\" = the worker uploads a file. \"signature\" = the worker reads a document, types their full name and signs with their passkey (Face ID / fingerprint); the name is signed with it (attach the document with signature_doc_base64 and signature_doc_mime). Most info-collection tasks should be \"text\", most verification tasks should be \"photo\"."
},
"requires_photo": {
"type": "boolean",
"description": "Require photo on completion"
},
"response_config": {
"type": "object",
"description": "Response configuration. For \"choice\": {options: [\"A\",\"B\",\"C\"]}. For \"text\": {placeholder, multiline}. For \"number\": {placeholder}. For \"date\": {label, include_text}."
},
"required_equipment": {
"type": "string",
"description": "Equipment this task depends on (free text, e.g. \"88 lb kettlebells\")"
},
"signature_doc_mime": {
"enum": [
"application/pdf",
"image/png",
"image/jpeg"
],
"type": "string",
"description": "Media type of signature_doc_base64."
},
"is_safety_checkpoint": {
"type": "boolean",
"description": "Safety-critical task flag"
},
"signature_doc_base64": {
"type": "string",
"description": "Only with response_type signature: the document to be signed, base64 (PDF, PNG or JPEG, at most 10 MB). Uploaded after the task is created. Needs signature_doc_mime."
}
}
},
"description": "Array of tasks to create"
},
"list_id": {
"type": "string",
"description": "Task list (event) ID"
},
"idempotency_key": {
"type": "string",
"description": "B1: optional replay key (up to 1024 chars, no braces) sent verbatim as the Idempotency-Key header instead of a fresh one per call — a retried call with the SAME key replays the original response instead of creating the tasks twice."
}
}
}Step 1 of the Closed-Loop Autonomous Operations Protocol. Retrieves full issue context including worker info, message thread, project history, and recent similar issues. Use this data to reason about the root cause and generate a remediation plan. Also supports server-side AI analysis via POST (calls Anthropic API directly).
| Argument | Type | Description |
|---|---|---|
| issue_id* | string | Issue ID to analyze |
| server_side_ai | boolean | If true, the server calls Anthropic API directly for AI analysis (default: false — returns raw data for MCP client to analyze) |
{
"type": "object",
"required": [
"issue_id"
],
"properties": {
"issue_id": {
"type": "string",
"description": "Issue ID to analyze"
},
"server_side_ai": {
"type": "boolean",
"description": "If true, the server calls Anthropic API directly for AI analysis (default: false — returns raw data for MCP client to analyze)"
}
}
}Apply a pre-built template to a task list, adding all template tasks
| Argument | Type | Description |
|---|---|---|
| list_id* | string | Task list (event) ID |
| template_slug* | string | Template slug (e.g. "conference-load-in", "warehouse-receiving") |
{
"type": "object",
"required": [
"list_id",
"template_slug"
],
"properties": {
"list_id": {
"type": "string",
"description": "Task list (event) ID"
},
"template_slug": {
"type": "string",
"description": "Template slug (e.g. \"conference-load-in\", \"warehouse-receiving\")"
}
}
}Run an AI condition assessment of an asset from a photo. The model scores 0-100 with the asset's full assessment history in context, so it reads degradation over time — returning the Condition Delta Score vs the previous assessment, defects, wear indicators, maintenance recommendations, and a degradation trajectory. Sensor-free predictive maintenance.
| Argument | Type | Description |
|---|---|---|
| asset_id* | string | Asset ID (from tascan_register_asset or tascan_list_assets) |
| photo_url* | string | Public URL of the assessment photo |
| worker_name | string | Who took the photo (optional) |
{
"type": "object",
"required": [
"asset_id",
"photo_url"
],
"properties": {
"asset_id": {
"type": "string",
"description": "Asset ID (from tascan_register_asset or tascan_list_assets)"
},
"photo_url": {
"type": "string",
"description": "Public URL of the assessment photo"
},
"worker_name": {
"type": "string",
"description": "Who took the photo (optional)"
}
}
}FULL Closed-Loop Autonomous Operations Protocol in one call. Server-side AI analyzes the issue, generates remediation tasks, creates a task list, and dispatches to the worker — all without human intervention. This executes Patent Claim 7: autonomous operations from issue detection through physical-world instruction delivery.
| Argument | Type | Description |
|---|---|---|
| issue_id* | string | Issue ID to auto-resolve |
{
"type": "object",
"required": [
"issue_id"
],
"properties": {
"issue_id": {
"type": "string",
"description": "Issue ID to auto-resolve"
}
}
}Cancel a scheduled text that has not been sent yet (status pending). A row already sending, sent, failed or cancelled is refused (409) — a sent text cannot be recalled.
| Argument | Type | Description |
|---|---|---|
| id* | string | The scheduled_sms row id from tascan_schedule_sms / tascan_list_scheduled_sms |
{
"type": "object",
"required": [
"id"
],
"properties": {
"id": {
"type": "string",
"description": "The scheduled_sms row id from tascan_schedule_sms / tascan_list_scheduled_sms"
}
}
}Claim a fan-out seat task as a LOCAL subagent run (POST /runs/claim -> claim_agent_run(p_runner='local')). Only admitted when instance starts with "<your registered agent_id>:" -- the seat's own coord.agent_id, set when the fan-out was created (tascan_create_fanout seats[i].runner='local'). Any other instance is refused, claimed false with reason seat_not_yours -- this tool never claims a seat that is not yours, and never claims a cloud (research) seat at all (refused not_routable). Never mints a completion or a receipt by itself -- claiming only opens the run; work happens after, and tascan_finish_run is what leaves the signed receipt. Requires agent:dispatch:code. A foreign or unknown task_id is 404, never 403 (S2: another org's task never even appears to exist).
| Argument | Type | Description |
|---|---|---|
| task_id* | string | The seat task ID (UUID) -- from tascan_create_fanout's seats[] or tascan_get_fanout (view=status/report). |
| instance* | string | Your claim identity, "<agent_id>:<anything>" -- e.g. "researcher-07:host-1234". Must start with the seat's own registered agent_id or the claim is refused seat_not_yours. |
| ttl_seconds | integer | Optional lease length in seconds (60-86400, default 1800). Extend it later with tascan_heartbeat_run instead of claiming a longer one up front. |
{
"type": "object",
"required": [
"task_id",
"instance"
],
"properties": {
"task_id": {
"type": "string",
"description": "The seat task ID (UUID) -- from tascan_create_fanout's seats[] or tascan_get_fanout (view=status/report)."
},
"instance": {
"type": "string",
"description": "Your claim identity, \"<agent_id>:<anything>\" -- e.g. \"researcher-07:host-1234\". Must start with the seat's own registered agent_id or the claim is refused seat_not_yours."
},
"ttl_seconds": {
"type": "integer",
"description": "Optional lease length in seconds (60-86400, default 1800). Extend it later with tascan_heartbeat_run instead of claiming a longer one up front."
}
}
}Duplicate a project's STRUCTURE (POST /projects/:id/clone): its task lists (not a work order's own lists), their tasks (never coordination cards) with response types, photo, safety and signature settings, subtasks, and attachments by reference, plus the location, dates, labor rules and report options. Nothing that happened is copied: no completions, issues, receipts, assignments or access settings. The copy gets the next project number and the name "<name> (copy)" unless you pass one. All or nothing: the copy is one database transaction, so a failure leaves nothing behind. Write tier.
| Argument | Type | Description |
|---|---|---|
| name | string | Name of the copy, 1-200 characters (default "<name> (copy)") |
| project_id* | string | The project to copy |
{
"type": "object",
"required": [
"project_id"
],
"properties": {
"name": {
"type": "string",
"description": "Name of the copy, 1-200 characters (default \"<name> (copy)\")"
},
"project_id": {
"type": "string",
"description": "The project to copy"
}
}
}Complete a subtask, optionally recording a typed response_value (e.g. the weight or reps for that set). Each completion is timestamped, so consecutive set completions yield per-set durations. Returns progress including all_subtasks_complete — when true, complete the parent task with tascan_complete_task.
| Argument | Type | Description |
|---|---|---|
| notes | string | Optional notes |
| worker_id | string | Worker performing the completion (optional) |
| subtask_id* | string | Subtask ID to complete |
| response_value | string | Typed response value (for number/text/choice subtasks), e.g. "165" |
{
"type": "object",
"required": [
"subtask_id"
],
"properties": {
"notes": {
"type": "string",
"description": "Optional notes"
},
"worker_id": {
"type": "string",
"description": "Worker performing the completion (optional)"
},
"subtask_id": {
"type": "string",
"description": "Subtask ID to complete"
},
"response_value": {
"type": "string",
"description": "Typed response value (for number/text/choice subtasks), e.g. \"165\""
}
}
}Complete an ORDINARY task on behalf of a worker. Inserts a completion record and timer event. Use this to simulate or record task completions via the API. Coordination-cycle tasks (a CODE:/REVIEW: build or review, a Decision / Question / Integrate / Parked card on a project Decisions list — tasks that carry `coord`) are refused with 403 for every key tier: builds and reviews are completed by their runner, decisions only by the human on the worker page.
| Argument | Type | Description |
|---|---|---|
| notes | string | Optional completion notes |
| task_id* | string | Task ID to complete |
| worker_id* | string | Worker ID performing the completion |
| response_value | string | Response value (for text/number/choice tasks) |
{
"type": "object",
"required": [
"task_id",
"worker_id"
],
"properties": {
"notes": {
"type": "string",
"description": "Optional completion notes"
},
"task_id": {
"type": "string",
"description": "Task ID to complete"
},
"worker_id": {
"type": "string",
"description": "Worker ID performing the completion"
},
"response_value": {
"type": "string",
"description": "Response value (for text/number/choice tasks)"
}
}
}Get an asset's longitudinal condition history — score trend over time, every assessment with grade, delta, findings, and who assessed it. The per-serial-number condition ledger.
| Argument | Type | Description |
|---|---|---|
| asset_id* | string | Asset ID |
{
"type": "object",
"required": [
"asset_id"
],
"properties": {
"asset_id": {
"type": "string",
"description": "Asset ID"
}
}
}Control a running fan-out (POST /coord/fanouts/:id/control). pause holds every unclaimed seat and stops re-firing; resume releases them; cancel supersedes every unclaimed task and closes once quiet; close_barrier drops unsettled (or named exclude_seats) seats and forces the synth now; retry_synth mints a new synth revision from the stored inputs of the current synth when every failed run is input_ref_mismatch, refused otherwise (not_synthesizing, no_synth_task, no_failed_runs, not_input_ref_mismatch). No amend or answer yet. Every action first records a signed control completion as the system worker of this org, never as another org identity. idempotency_key replays the prior outcome (replayed true) and applies nothing twice; a replayed resume after a later pause stays paused. Refused fanout_closed once the roll-up has closed. Requires agent:dispatch:code.
| Argument | Type | Description |
|---|---|---|
| action* | string | pause, resume, cancel, close_barrier or retry_synth. No amend, no answer (phase 1). |
| reason | string | Optional free-text reason (<= 4000 chars), recorded on the control child's completion. |
| fanout_id* | string | Fan-out id (UUID) — the T_F root. |
| exclude_seats | array | close_barrier only: 1-based seat numbers to drop from the barrier instead of waiting for them to settle. |
| idempotency_key | string | Optional replay key (<= 200 chars). The same key returns the prior outcome (replayed:true) and applies nothing a second time. |
Start an unattended build-review-decide cycle (protocol v0.2). Queues T1 CODE: (or SHELL:) with your build_brief on the AI Inbox and T2 REVIEW: with your review_brief, born blocked on T1. T1 (2026-09-24 throughput): kind=review_only mints T1 as CAPTURE: instead — the executor stores the bundle from the repo at HEAD without a model call and the review runs on that; dry_run=true runs the dispatch PREFLIGHT only (nothing queued) and prints every problem at once (codes title_too_long, brief_rule, brief_names_unbundled_path, artifact_over_cap, migration_as_context (tascan repo only), idempotency_replay, kind_invalid — bad kind, bad revisable_by, review_only + task_type SHELL/RESEARCH, review_only + max_questions 0), then what the API could NOT check (unchecked[]: artifact_missing_at_head always — only the executor sees the repo, and a CAPTURE naming a missing path fails with "[artifact_missing_at_head]" in its error; artifact_over_cap for any path without a byte count — pass artifact_bytes; idempotency_replay only if its lookup failed) and warnings[] (a build_brief naming a context file outside the bundle); a real create that the API refuses prints the same problems[] list (when the transport hands the tool only the first problem's text, the tool re-runs the preflight and prints the whole list). The local executor builds, stores the exact bytes of artifact_paths as a bundle (build_ref = sha256 over the manifest), the independent reviewer reviews THAT bundle, an approve verdict mints a Decision task for the human authority (one SMS), Approve mints an Integrate task for the deploy id. Revise verdicts spawn revisions (cap max_revisions, default 3); reject, human Reject, scope violations or exhausted revisions PARK the cycle (a Parked task with Resume with notes / Close). REQUIRES agent:dispatch:code. Duplicate protection: the same idempotency_key, or (keyless) the same briefs + paths on a live root, within 24 h is refused by the preflight as idempotency_replay (400, nothing queued, the existing root_id in the problem) and printed as DUPLICATE with that root_id; only a replay the preflight could not see (a race) comes back from the RPC as 200 created=false, printed the same way. Optional `reviews[]` attaches a multi-lens review panel (design item 14a) in place of the single OpenAI review — one review task mints per lens and every blocking lens must approve before the Decision task mints. Track with tascan_get_cycle_report (root_id). Nothing spawns a cycle on its own.
| Argument |
|---|
Create a new event (task list) within a project. Supports team_mode (shared completions) and multi_instance (each worker gets isolated copy — great for surveys, onboarding, info collection). team_mode and multi_instance cannot both be true.
| Argument | Type | Description |
|---|---|---|
| name* | string | Event name |
| team_mode | boolean | Team mode — shared completions |
| project_id* | string | Project ID |
| description | string | Event description |
| multi_instance | boolean | Multi-instance — each worker gets isolated copy |
| assigned_worker_ids | array | Worker UUIDs to assign to this event. Each gets a personal tap-to-open link. Note: a worker holds one event assignment per project — assigning moves them. |
{
"type": "object",
"required": [
"project_id",
"name"
],
"properties": {
"name": {
"type": "string",
"description": "Event name"
},
"team_mode": {
"type": "boolean",
"description": "Team mode — shared completions"
},
"project_id": {
"type": "string",
"description": "Project ID"
},
"description": {
"type": "string",
"description": "Event description"
},
"multi_instance": {
"type": "boolean",
"description": "Multi-instance — each worker gets isolated copy"
},
"assigned_worker_ids": {
"type": "array",
"items": {
"type": "string"
},
"description": "Worker UUIDs to assign to this event. Each gets a personal tap-to-open link. Note: a worker holds one event assignment per project — assigning moves them."
}
}
}Create a fan-out: 1-30 seats (RESEARCH by default, or LOCAL subagent seats) plus one synthesizer under ONE cycle root (POST /coord/fanouts). No Decision cards, Parked cards or pages per seat: only a control action or the closing roll-up reaches a human. Needs an API key with agent:dispatch:code (an admin session has no dispatch budget: key_required). dry_run true runs the same validation and ceiling/cap pre-check and returns dry_run, plan_sha256, seats and problems without spending budget or creating anything. Otherwise it consumes seats + 1 dispatch units BEFORE the create; a refusal only the database can see (cap_below_plan, fanout_open_limit, bad agent_id, reserve_floor_unset) still consumes them, by design. Fails fanout_ceiling_unset until the owner sets a ceiling, over_ceiling when cap exceeds it. idempotency_key replays a duplicate within 24h on the same plan, else idempotency_body_mismatch. Every seat, the synth and the roll-up leave a signed completion. Track with tascan_get_fanout.
| Argument | Type | Description |
|---|---|---|
| cap* | integer | This fan-out's total micro-USD budget, synth reserve included. Refused over_ceiling if it exceeds the org's fan-out spending ceiling. |
| seats* | array | 1-30 seats. Each names the agent that runs it; brief/role fall back to seat_defaults when omitted. |
| synth* | object | The synthesizer that runs once the barrier closes, reading every done seat's stored output as DATA. |
| title* | string | Short human title (1-200 chars). T_F becomes "FANOUT: <title>"; seats become "RESEARCH: [seat-NN] <title>"; the synth becomes "RESEARCH: [synth] <title>". |
Create a client invoice and get its shareable link. Two ways to bill: (a) pass explicit line_items, or (b) pass project_id or task_list_ids plus hourly_rate (quarter-hour billing from first→last verified completion per list) or flat_rate_per_list, and TaScan builds one line per list from VERIFIED work ("<list> — 7/7 tasks verified · Sep 1 · 1.25h"); lists with no completions are skipped. A single-list invoice also mints a client-facing Service Report (acknowledge → pay) and links it. Returns invoice number, totals, url, and the work it billed.
| Argument | Type | Description |
|---|---|---|
| notes | string | Payment terms / thank-you shown on the invoice |
| status | string | Default sent |
| billing | object | Billing rules for auto line items. mode: hourly (default when hourly_rate given) | day_rate | flat. Overtime/double time are computed PER WORK DAY from verified completions: hours over overtime_after_hours (default 8) bill at overtime_multiplier (1.5×), hours over double_time_after_hours (12) at double_time_multiplier (2×); set overtime:false to disable. per_diem adds one line × work days (or per_diem_days). expenses are pass-through lines. |
| due_date | string | YYYY-MM-DD (default: 30 days out) |
| tax_rate | number | Fraction, e.g. 0.0825 for 8.25% |
| min_hours | number | Minimum billable hours per list (e.g. 1) |
| line_items |
Create a new TaScan project (top-level container for events). number is optional (the next P-number is assigned when omitted; a number already used in the org is refused 409). external_refs links customer job numbers.
| Argument | Type | Description |
|---|---|---|
| name* | string | Project name |
| number | string | Project number, 1-40 letters, digits, dot, underscore or hyphen, unique in the org (default: next P-00001 style number). P-<digits> numbers are assigned automatically and refused 400 here; use your own format (e.g. P14-4680) |
| location | string | Project location / venue |
| external_refs | array | Up to 10 external references, each { source (1-40 chars, e.g. "EEQ"), ref (1-80 chars, e.g. "P14-4680") } |
{
"type": "object",
"required": [
"name"
],
"properties": {
"name": {
"type": "string",
"description": "Project name"
},
"number": {
"type": "string",
"description": "Project number, 1-40 letters, digits, dot, underscore or hyphen, unique in the org (default: next P-00001 style number). P-<digits> numbers are assigned automatically and refused 400 here; use your own format (e.g. P14-4680)"
},
"location": {
"type": "string",
"description": "Project location / venue"
},
"external_refs": {
"type": "array",
"items": {
"type": "object",
"required": [
"source",
"ref"
],
"properties": {
"ref": {
"type": "string"
},
"source": {
"type": "string"
}
}
},
"description": "Up to 10 external references, each { source (1-40 chars, e.g. \"EEQ\"), ref (1-80 chars, e.g. \"P14-4680\") }"
}
}
}Create a site (a job location) in the organization: name (1-200 characters) and optionally address, lat + lng (together), client_company and external_key (unique in your org: a second site with the same key is refused 409). Write tier.
| Argument | Type | Description |
|---|---|---|
| lat | number | -90 to 90 (send with lng) |
| lng | number | -180 to 180 (send with lat) |
| name* | string | Site name, 1-200 characters |
| address | string | |
| external_key | string | Your own id for the site (e.g. a customer site number), unique in your org |
| client_company | string |
{
"type": "object",
"required": [
"name"
],
"properties": {
"lat": {
"type": "number",
"description": "-90 to 90 (send with lng)"
},
"lng": {
"type": "number",
"description": "-180 to 180 (send with lat)"
},
"name": {
"type": "string",
"description": "Site name, 1-200 characters"
},
"address": {
"type": "string"
},
"external_key": {
"type": "string",
"description": "Your own id for the site (e.g. a customer site number), unique in your org"
},
"client_company": {
"type": "string"
}
}
}Create a new worker (taskee) in the organization
| Argument | Type | Description |
|---|---|---|
| name* | string | Worker name |
| string | ||
| phone | string | Phone number |
{
"type": "object",
"required": [
"name"
],
"properties": {
"name": {
"type": "string",
"description": "Worker name"
},
"email": {
"type": "string",
"description": "Email"
},
"phone": {
"type": "string",
"description": "Phone number"
}
}
}Create a geofenced work zone. Delivery zones route workers who open the project Site Gate (geo.html?project=...) to this zone's task list when GPS places them inside the radius. Set enforce_on_list=true to zone-lock the task list — workers cannot start it from outside the zone.
| Argument | Type | Description |
|---|---|---|
| lat* | number | Zone center latitude |
| lng* | number | Zone center longitude |
| kind | string | What the fence MEANS. work_site: expected here (auto clock-in, list on enter). hazard: enter allowed under conditions — required_ppe + photo checkpoint verified by AI vision, the OSHA row. containment: must stay inside; leaving = breach. restricted: must stay out; entering = breach. Default work_site. |
| name* | string | Zone name (e.g. "Stage Left", "Loading Dock") |
| polygon | array | Polygon/rectangle zone instead of a circle: vertices as [[lat,lng], ...], at least 3. lat/lng/radius_m are then computed (centroid + bounding radius) — still pass lat/lng but they are overridden. |
| radius_m | number | Radius in meters (default 150, min 10, max 100000) |
| project_id | string | Project this zone belongs to |
Mint a time-limited, NARROWER child API key (POST /delegations, roadmap #11) — only read/write scopes may be requested; full, agent:dispatch, agent:dispatch:code, device:admin, delegate and webhooks:manage are always refused by name (I-6). TTL defaults to 1 hour (3600s), clamps to [60 seconds, 24 hours], and clamps further to THIS key's own expiry if it has one — a delegated key can never outlive its grantor. The child's tier is always a SUBSET of this key's own effective tier, never wider or lateral. Requires the delegate permission (owner-only to grant, requires full). The returned raw key is shown to you exactly ONCE — it is never stored server-side and cannot be retrieved again (I-10). Revoking this key (or any ancestor) cascades immediately to every key it delegated.
| Argument | Type | Description |
|---|---|---|
| role | string | The agent’s job on the project: builder, researcher, reviewer, panelist, ... |
| model | string | The model the agent runs on, e.g. claude-sonnet-5-5. |
| scopes* | array | Scopes to request for the child key — read and/or write only. |
| vendor | string | The model vendor (defaults to this key’s own vendor). |
| agent_id | string | Stable agent id used in receipts and the agent registry. |
| ttl_seconds | number | How long the child key lives, in seconds (default 3600 = 1h; clamped to [60, 86400] and to this key's own expiry). |
Delete an event (task list) and all its tasks and completions. This action is irreversible.
| Argument | Type | Description |
|---|---|---|
| list_id* | string | Task list (event) ID to delete |
{
"type": "object",
"required": [
"list_id"
],
"properties": {
"list_id": {
"type": "string",
"description": "Task list (event) ID to delete"
}
}
}Delete a project that has NO history, after typing its name. Refused 409 project_has_history (with the counts) when it has any task or subtask completion, action receipt, issue, invoice or coordination task: archive it instead (tascan_update_project status archived). confirm_name must equal the project name exactly (400 confirm_name_mismatch). A receipted audit event (actor, project, name, counts, your audit note) is written BEFORE the delete; if it cannot be written nothing is deleted. The check, the audit event and the delete are one database transaction that locks receipts, tasks and the project first, so history created meanwhile is never deleted (503 busy, nothing deleted, when those locks are not had within 3 seconds: retry). Irreversible.
| Argument | Type | Description |
|---|---|---|
| audit | object | Optional audit note stored verbatim in the audit event (e.g. { reason }) |
| project_id* | string | Project ID to delete |
| confirm_name* | string | The project name, typed exactly |
{
"type": "object",
"required": [
"project_id",
"confirm_name"
],
"properties": {
"audit": {
"type": "object",
"description": "Optional audit note stored verbatim in the audit event (e.g. { reason })"
},
"project_id": {
"type": "string",
"description": "Project ID to delete"
},
"confirm_name": {
"type": "string",
"description": "The project name, typed exactly"
}
}
}Delete a subtask and its completions. This action is irreversible.
| Argument | Type | Description |
|---|---|---|
| subtask_id* | string | Subtask ID to delete |
{
"type": "object",
"required": [
"subtask_id"
],
"properties": {
"subtask_id": {
"type": "string",
"description": "Subtask ID to delete"
}
}
}Delete a specific task and its completions. This action is irreversible.
| Argument | Type | Description |
|---|---|---|
| task_id* | string | Task ID to delete |
{
"type": "object",
"required": [
"task_id"
],
"properties": {
"task_id": {
"type": "string",
"description": "Task ID to delete"
}
}
}Tombstone a worker record (is_active=false). REFUSES if the worker has any task/subtask completions or payments — merge those into the real worker with tascan_merge_workers instead. Never hard-deletes.
| Argument | Type | Description |
|---|---|---|
| worker_id* | string | Worker ID to delete |
{
"type": "object",
"required": [
"worker_id"
],
"properties": {
"worker_id": {
"type": "string",
"description": "Worker ID to delete"
}
}
}Step 3 of the Closed-Loop Autonomous Operations Protocol. Dispatches remediation to the worker via MULTI-CHANNEL delivery: (1) issue thread message, (2) in-app notification, (3) progress feed update, (4) SMS if phone on file, (5) optional remediation task list creation. Closes the loop from digital AI analysis to physical worker execution.
| Argument | Type | Description |
|---|---|---|
| ai_agent | string | Name of the AI agent dispatching (default: TaScan AI) |
| issue_id* | string | Issue ID this instruction relates to |
| send_sms | boolean | Send SMS to worker (default: true if phone on file) |
| worker_id | string | Target worker ID (defaults to the worker who reported the issue) |
| instruction* | string | Clear, actionable instruction for the worker to execute |
| remediation_tasks | array | Optional array of tasks to create as a remediation task list. Each: { title, description, response_type, requires_photo, is_safety_checkpoint, sort_order } |
| recommendation_summary | string | One-line summary for the task list description |
PREFERRED tool for sending work to an AI agent. Dispatches a task to the agent's inbox — picked up and executed automatically. No list ID needed. REQUIRES the agent:dispatch permission on this connection (CODE:/SHELL: tasks also require agent:dispatch:code) — reconnect and tick the agent checkbox(es) if refused. Routing is by TITLE PREFIX only: CODE: SHELL: PLAN: MCP: → local Claude Code on Mike's PC; RESEARCH: WRITE: REVIEW: → cloud; no prefix (DEFAULT) → local while the PC agent is alive, else cloud. The cloud agent refuses CODE/SHELL/PLAN/MCP. Use "agent" param to target a specific agent (default: claude-code-local). Use tascan_list_agents to discover available agents; track progress with tascan_get_task (its "agent" block).
| Argument | Type | Description |
|---|---|---|
| task* | string | The full task text. START it with CODE: SHELL: PLAN: MCP: RESEARCH: WRITE: or REVIEW: for routing (prefix-only — nothing may precede it), or leave it unprefixed (DEFAULT). The whole text is stored in the task description; the first 140 chars become the title. |
| agent | string | Agent ID or name to dispatch to (default: claude-code-local). Use tascan_list_agents to see options. An unknown agent is an error, never a silent fallback. |
| priority | string | Priority level (default: normal). The urgent marker is placed AFTER the routing prefix so it never breaks routing. |
{
"type": "object",
"required": [
"task"
],
"properties": {
"task": {
"type": "string",
"description": "The full task text. START it with CODE: SHELL: PLAN: MCP: RESEARCH: WRITE: or REVIEW: for routing (prefix-only — nothing may precede it), or leave it unprefixed (DEFAULT). The whole text is stored in the task description; the first 140 chars become the title."
},
"agent": {
"type": "string",
"description": "Agent ID or name to dispatch to (default: claude-code-local). Use tascan_list_agents to see options. An unknown agent is an error, never a silent fallback."
},
"priority": {
"enum": [
"normal",
"urgent"
],
"type": "string",
"description": "Priority level (default: normal). The urgent marker is placed AFTER the routing prefix so it never breaks routing."
}
}
}Record a chief-of-staff dispatcher action on a coordination cycle as a receipt (protocol v0.2 D6, POST /coord/cycles/:root/dispatcher-actions): hand_review_approve, hand_review_revise, park, bundle_recovery, migration_apply, deploy or decision. Mints a completed task + completion under the Chief of Staff worker and a dispatcher_action ledger event on the root. kind=deploy additionally requires evidence.deploy_id (a Netlify deploy id) and composes the same integrate-as-note every root gets with coord_record_integration when the root is authorized — one call that does both records. Requires agent:dispatch.
| Argument | Type | Description |
|---|---|---|
| kind* | string | Which dispatcher action this is. |
| root_id* | string | The cycle root task id (tascan_list_cycles / tascan_create_cycle). |
| summary* | string | Human-readable summary (1-4000 chars) — becomes the recorded completion notes. |
| evidence | object | Optional evidence object (serializes to at most 8000 chars). For kind=deploy this must include deploy_id: 24 lowercase hex, optionally prefixed deployed: and/or followed by a build-ref hex prefix. |
{
"type": "object",
"required": [
"root_id",
"kind",
"summary"
],
"properties": {
"kind": {
"enum": [
"hand_review_approve",
"hand_review_revise",
"park",
"bundle_recovery",
"migration_apply",
"deploy",
"decision"
],
"type": "string",
"description": "Which dispatcher action this is."
},
"root_id": {
"type": "string",
"description": "The cycle root task id (tascan_list_cycles / tascan_create_cycle)."
},
"summary": {
"type": "string",
"description": "Human-readable summary (1-4000 chars) — becomes the recorded completion notes."
},
"evidence": {
"type": "object",
"description": "Optional evidence object (serializes to at most 8000 chars). For kind=deploy this must include deploy_id: 24 lowercase hex, optionally prefixed deployed: and/or followed by a build-ref hex prefix."
}
}
}Read or author a task's evidence policy (protocol v0.2 V9b, GET|PUT /evidence/policy/:task_id — one route, two methods, so one tool). action=get evaluates the pinned policy against the evidence ledger so far: whether it is usable and satisfied, and its progress (read tier). action=set authors/updates a policy in your org's namespace when definition is given (definition.require is a non-empty array of requirement objects; definition.min_count, when set, must be an integer from 1 through definition.require.length) or, when definition is omitted, pins the task to an existing active policy_id (write tier).
| Argument | Type | Description |
|---|---|---|
| action* | string | get = GET (evaluate, read tier). set = PUT (author/pin, write tier). |
| task_id* | string | Task ID (UUID) in your organization. |
| version | integer | action=set only: optional version for a new/updated policy definition (default 1). Ignored when only pinning an existing policy. |
| policy_id | string | action=set only, required: lowercase letters/digits/underscore/dot, starting with a letter, up to 80 chars, e.g. loto_v1. |
| definition | object | action=set only, optional: policy definition to create or update — require (array, required), min_count, window, any_of, allow_fallback. Omit to pin the task to the existing active policy_id instead. |
{
"type": "object",
"required": [
"action",
"task_id"
],
"properties": {
"action": {
"enum": [
"get",
"set"
],
"type": "string",
"description": "get = GET (evaluate, read tier). set = PUT (author/pin, write tier)."
},
"task_id": {
"type": "string",
"description": "Task ID (UUID) in your organization."
},
"version": {
"type": "integer",
"description": "action=set only: optional version for a new/updated policy definition (default 1). Ignored when only pinning an existing policy."
},
"policy_id": {
"type": "string",
"description": "action=set only, required: lowercase letters/digits/underscore/dot, starting with a letter, up to 80 chars, e.g. loto_v1."
},
"definition": {
"type": "object",
"description": "action=set only, optional: policy definition to create or update — require (array, required), min_count, window, any_of, allow_fallback. Omit to pin the task to the existing active policy_id instead."
}
}
}Cross-entity search: find projects (by name, project number or customer job number in external_refs; each with its status), task lists, tasks, workers, condition assets, sites (by name, address or client company) or work orders (by number or description) in one call — with ids and parent context to disambiguate. Use this instead of walking projects→lists→tasks or guessing ids from display names.
| Argument | Type | Description |
|---|---|---|
| type | string | Optional: restrict to one entity type |
| query* | string | Search text (min 2 chars, case-insensitive substring; a customer job number matches external_refs exactly) |
{
"type": "object",
"required": [
"query"
],
"properties": {
"type": {
"enum": [
"project",
"list",
"task",
"worker",
"asset",
"site",
"work_order"
],
"type": "string",
"description": "Optional: restrict to one entity type"
},
"query": {
"type": "string",
"description": "Search text (min 2 chars, case-insensitive substring; a customer job number matches external_refs exactly)"
}
}
}Find candidate same-person worker records with per-signal match detail (Patent 4 §6.25(b) signals: phone reuse, name similarity, GPS pattern correlation). Turns identity fragmentation from an accidental discovery into a monitorable metric, and feeds the merge workflow its candidate list.
| Argument | Type | Description |
|---|---|---|
| name | string | Or: search duplicates by display name |
| threshold | number | Min confidence 0-1 (default 0.15) |
| worker_id | string | Anchor worker to find duplicates OF (preferred — enables phone + GPS signals) |
| include_orphans | boolean | Also consider org-less (orphan) worker records — off by default |
{
"type": "object",
"required": [],
"properties": {
"name": {
"type": "string",
"description": "Or: search duplicates by display name"
},
"threshold": {
"type": "number",
"description": "Min confidence 0-1 (default 0.15)"
},
"worker_id": {
"type": "string",
"description": "Anchor worker to find duplicates OF (preferred — enables phone + GPS signals)"
},
"include_orphans": {
"type": "boolean",
"description": "Also consider org-less (orphan) worker records — off by default"
}
}
}Finish a claimed local run (POST /runs/:run_id/finish). outcome='completed' stores `document` (and optional `verification`) as the seat's own build artifacts under EXACTLY the artifact_paths the fan-out named for this seat, hashes them into build_ref, and finishes the run -- this mints a real task_completions row and a SIGNED TASCAN RECEIPT for your work (tascan_get_receipt), the same as any other build; `document` is your deliverable and IS what gets hashed, so send the real content, not a summary. Zero changes to coord_fanout_settle / the barrier / the roll-up -- your completion releases the next held seat and, once every seat settles, feeds the fan-out's own synthesizer and rollup exactly like a cloud research seat's would. outcome='failed' finishes the run failed with your `error` text and stores nothing. Requires agent:dispatch:code. A run outside your org, or already finished, is refused (404 / the RPC's own not_live).
| Argument | Type | Description |
|---|---|---|
| error | string | Required when outcome is failed -- why (up to 4000 chars). |
| run_id* | string | The run ID from tascan_claim_run's response. |
| outcome* | string | 'completed' stores your document and mints a signed completion; 'failed' stores nothing. |
| document | string | Required when outcome is completed. The seat's deliverable -- hashed as build_ref, stored verbatim under the seat's own artifact_paths. |
| instance* | string | The SAME instance string you claimed with. |
Generate a QR code for a task list (event) that workers can scan to access tasks. TaScan draws the QR itself (an SVG, also returned as a data: URI in qr_image_url); no third-party QR service ever sees the link.
| Argument | Type | Description |
|---|---|---|
| style | string | Reserved for saved QR styles (SR-P4c): accepted and ignored today |
| list_id* | string | Task list (event) ID |
{
"type": "object",
"required": [
"list_id"
],
"properties": {
"style": {
"type": "string",
"description": "Reserved for saved QR styles (SR-P4c): accepted and ignored today"
},
"list_id": {
"type": "string",
"description": "Task list (event) ID"
}
}
}Mint a shareable report and get its link. Types: completion (full proof-of-work for one list: tasks, responses, subtasks, photos, GPS + place names, timing, QR pair), service (client-facing version of a list with YOUR company branding and a Client Acknowledgment button — the ack files into the list thread), project (every list in a project rolled up), evidence (compliance Evidence Pack; admin sign-in required to view). Links are stable — the same list/project returns the same link. Optionally text the link to a phone through the TaScan SMS lane.
| Argument | Type | Description |
|---|---|---|
| type* | string | |
| list_id | string | Required for completion / service |
| message | string | Service report: a note to the client shown under the header |
| send_note | string | Short intro for the text, e.g. "Here is your report from Love Productions:" |
| project_id | string | Required for project / evidence |
| show_issues | boolean | Service report: include reported issues (default false) |
| company_name | string | Service report branding (defaults to the org name) |
| show_workers | boolean |
Read THIS credential's own per-key action budget (GET /budget): daily limits (sms, invites, payment-request cents per day and per request, dispatch, human pages) and today's spend against them. A NULL limit (or no budget row at all) means unlimited on that counter. A key can only ever see its OWN budget, never another key's (I-3, no tool or key ever reads another key's row). Read tier.
No arguments.
{
"type": "object",
"properties": {}
}Manifest of a stored build bundle by build_ref (protocol v0.2 build_artifacts): the exact files the executor produced for the cycle's artifact_paths, each with sha256, byte length and whether text content is stored (binary or over-cap files keep the sha only). build_ref = sha256 over the manifest, computed in the database once; the reviewer reviews THESE bytes, the human approves THIS ref, the integrate task records THIS ref. Read tier. Use tascan_get_build_file to read a file. Truncated at 12000 chars. Reading the manifest is discovery, not a read of any file.
| Argument | Type | Description |
|---|---|---|
| build_ref* | string | sha256:<64 hex> (or the bare 64 hex) — from tascan_get_task (coord.build_ref / agent.runs[].build_ref) or tascan_get_cycle_report. |
{
"type": "object",
"required": [
"build_ref"
],
"properties": {
"build_ref": {
"type": "string",
"description": "sha256:<64 hex> (or the bare 64 hex) — from tascan_get_task (coord.build_ref / agent.runs[].build_ref) or tascan_get_cycle_report."
}
}
}Store an already-computed diff for one file of a build bundle against a base commit (protocol v0.2 item 6, POST /coord/builds/:build_ref/diff — the parser has no GET for this path; a deployed function ships no git object database, so tascan-agent/deployer.js computes the diff text with its own persistent worktree and this route only validates + stores it via coord_set_artifact_diff). build_sha256 must equal the artifact's own stored sha256 or the call is refused; storing a diff replaces that file's stored full content with the diff going forward. There is currently no REST route that reads a stored diff back (GET /builds/:build_ref and /builds/:build_ref/file do not surface it) — this tool only writes one. Requires agent:dispatch.
| Argument | Type | Description |
|---|---|---|
| path* | string | Repo-relative path from the build manifest (tascan_get_build). |
| text* | string | The already-computed diff text (up to 262144 characters). This tool never computes a diff itself. |
| base_ref* | string | The commit-ish the diff was computed against (1-200 chars). |
| build_ref* | string | sha256:<64 hex> (or the bare 64 hex) of the build the diff is about. |
| truncated | boolean | True if text is itself a truncated diff. |
| base_sha256 |
Read one file from a stored build bundle by build_ref and path (protocol v0.2 build_artifacts) — the exact bytes the executor produced, not a working-tree read. Returns up to 12000 chars per call with offset/limit paging (next_offset when truncated), plus the file's sha256 and byte length. Binary or over-cap files return no content (the sha256 still binds them). Read tier; this is what the independent reviewer reads. The header lines (path, build, sha256, chars a-b of total) are the record a reviewer's read is bound to; read every chunk until the range covers the whole file.
| Argument | Type | Description |
|---|---|---|
| path* | string | Repo-relative path exactly as listed by tascan_get_build. |
| limit | integer | Characters to return (1-12000, default 12000). |
| offset | integer | Character offset to start from (default 0). |
| build_ref* | string | sha256:<64 hex> (or the bare 64 hex). |
{
"type": "object",
"required": [
"build_ref",
"path"
],
"properties": {
"path": {
"type": "string",
"description": "Repo-relative path exactly as listed by tascan_get_build."
},
"limit": {
"type": "integer",
"description": "Characters to return (1-12000, default 12000)."
},
"offset": {
"type": "integer",
"description": "Character offset to start from (default 0)."
},
"build_ref": {
"type": "string",
"description": "sha256:<64 hex> (or the bare 64 hex)."
}
}
}The audit report of one coordination cycle by its root task id (protocol v0.2, get_cycle_report): every step task (build, review, checkpoint, integrate, question, parked) with its revision and state, every execution attempt with runner, outcome, build_ref and usage/cost, every completion (receipt id = completion id, receipt hash), every reviewer verdict, every human decision and answer, the full trail (messages), the ledger events and the hash-chain verdict per task, plus spend against the cap. A computed summary (stage, attempts, verdicts, decisions, receipts, spend, chains_ok) comes first; pass full=true for the complete JSON (large). Read tier. This is the ONLY per-cycle notification surface: cycle steps do not e-mail or text anyone except the one checkpoint / human-question SMS.
| Argument | Type | Description |
|---|---|---|
| full | boolean | true = the complete report JSON (tasks, runs, completions, messages, events, bundles, chains) after the summary, capped at 12000 chars. Default: summary only. |
| root_id* | string | The cycle root = the T1 build task id (returned by tascan_create_cycle; a non-root cycle task returns its root_id in the error). |
{
"type": "object",
"required": [
"root_id"
],
"properties": {
"full": {
"type": "boolean",
"description": "true = the complete report JSON (tasks, runs, completions, messages, events, bundles, chains) after the summary, capped at 12000 chars. Default: summary only."
},
"root_id": {
"type": "string",
"description": "The cycle root = the T1 build task id (returned by tascan_create_cycle; a non-root cycle task returns its root_id in the error)."
}
}
}Get one device by id: kind, name, status, location zone, credential rotation timestamps, revocation reason, and the last 20 evidence_events ids it produced. Never returns a credential hash.
| Argument | Type | Description |
|---|---|---|
| device_id* | string | Device ID |
{
"type": "object",
"required": [
"device_id"
],
"properties": {
"device_id": {
"type": "string",
"description": "Device ID"
}
}
}Get details of a specific event (task list) including its tasks
| Argument | Type | Description |
|---|---|---|
| list_id* | string | Task list (event) ID |
{
"type": "object",
"required": [
"list_id"
],
"properties": {
"list_id": {
"type": "string",
"description": "Task list (event) ID"
}
}
}Read a fan-out (GET /coord/fanouts/:id?view=). view=status (default): state, seat counts, spend, cap, synth_deadline_at, poll_after_s. view=report: the full cycle report (get_cycle_report) — every seat, review and control step. view=rollup: the exact roll-up response_value plus its leaves (path, sha256, completion_id) once closed. view=verify: the server recomputes rollup_ref over the frozen leaves and returns ok / mismatches — the same check scripts/verify-fanout.js performs independently from another machine, which is the trusted proof; this view is a convenience, not a substitute. A foreign org's fanout_id answers 404, never 403, so another org's fan-out never even appears to exist. Read tier, no dispatch permission required.
| Argument | Type | Description |
|---|---|---|
| view | string | status (default), report, rollup or verify. |
| fanout_id* | string | Fan-out id (UUID) — the T_F root, from tascan_create_fanout or tascan_list_cycles. |
{
"type": "object",
"required": [
"fanout_id"
],
"properties": {
"view": {
"enum": [
"status",
"report",
"rollup",
"verify"
],
"type": "string",
"description": "status (default), report, rollup or verify."
},
"fanout_id": {
"type": "string",
"description": "Fan-out id (UUID) — the T_F root, from tascan_create_fanout or tascan_list_cycles."
}
}
}Read the organization profile (GET /org): id, name, industry, logo, time zone and the admin phone MASKED to its last 4 digits (a tool never sees the full number). Read tier. Editing the organization is done by an owner or admin in the TaScan portal, not by any tool.
No arguments.
{
"type": "object",
"properties": {}
}Get details of a specific project
| Argument | Type | Description |
|---|---|---|
| project_id* | string | Project ID |
{
"type": "object",
"required": [
"project_id"
],
"properties": {
"project_id": {
"type": "string",
"description": "Project ID"
}
}
}Fetch the signed Action Receipt (Ed25519 JWS) for one completed task by completion_id (tascan_get_task -> completions[].id). Returns a readable summary (what, who, verification, evidence hashes, outcome, ledger chain) plus receipt_id/serial/kid, the compact JWS and the public verify URL. Verify offline against the JWKS or online by POSTing a JSON body whose jws field holds the compact receipt. Read outcome and verification separately: outcome completed = the executor returned and a result was recorded; verification.result = the verdict of a named policy; all-null verification with reason no_policy_run = no policy ran. Never treat outcome=completed as success without a policy verdict you trust (protocol 8.3 C11). Verifier: 6.8. profile=public returns the separately signed public export profile (protocol 6.10): it withholds the raw org, list, project, worker, run and trace ids (each a 16-hex id_hash) and storage locators, and binds to the full receipt - the form for anyone outside the org. Read tier.
| Argument | Type | Description |
|---|---|---|
| profile | string | full (default) = the org view with the unsigned private block; public = the separately signed public export profile: no raw org, list, project, worker, run or trace id (each is a 16-hex id_hash), no storage locators, plus a binding to the full receipt it was derived from - the form to hand to anyone outside the org |
| completion_id* | string | task_completions.id (UUID) - from tascan_get_task -> completions[].id |
{
"type": "object",
"required": [
"completion_id"
],
"properties": {
"profile": {
"enum": [
"full",
"public"
],
"type": "string",
"description": "full (default) = the org view with the unsigned private block; public = the separately signed public export profile: no raw org, list, project, worker, run or trace id (each is a 16-hex id_hash), no storage locators, plus a binding to the full receipt it was derived from - the form to hand to anyone outside the org"
},
"completion_id": {
"type": "string",
"description": "task_completions.id (UUID) - from tascan_get_task -> completions[].id"
}
}
}Get completion report for a task list (event) including task status, completions, workers, and photos. Set include_responses to also return the actual submitted response data (numbers, text, choices) for each completed task plus a per-task photos list with fetchable signed URLs (short-lived, ~1h) for the photo evidence.
| Argument | Type | Description |
|---|---|---|
| list_id* | string | Task list (event) ID |
| include_responses | boolean | Include the actual submitted response values for each completed task (default false — keeps the payload light) |
{
"type": "object",
"required": [
"list_id"
],
"properties": {
"list_id": {
"type": "string",
"description": "Task list (event) ID"
},
"include_responses": {
"type": "boolean",
"description": "Include the actual submitted response values for each completed task (default false — keeps the payload light)"
}
}
}Scan accountability data. Two modes: (1) tag_id — scans of a registered NFC tag; (2) task_list_id or project_id — every QR/link page-open stamp: when the code was scanned, GPS + IP + channel (qr/nfc/sms/email/link), who the scanner turned out to be, and the scan→start delta (how long between scanning and actually identifying + starting work — the sign-in-and-vanish metric).
| Argument | Type | Description |
|---|---|---|
| limit | number | Max results (default 50, max 200) |
| since | string | ISO timestamp — only scans after this |
| source | string | Filter page scans by channel: qr | nfc | sms | email | link | unknown |
| tag_id | string | NFC tag registry ID (from tascan_list_tags) — tag mode |
| project_id | string | Project ID — page-scan mode, all lists in the project |
| task_list_id | string | Task list ID — page-scan mode |
{
"type": "object",
"required": [],
"properties": {
"limit": {
"type": "number",
"description": "Max results (default 50, max 200)"
},
"since": {
"type": "string",
"description": "ISO timestamp — only scans after this"
},
"source": {
"type": "string",
"description": "Filter page scans by channel: qr | nfc | sms | email | link | unknown"
},
"tag_id": {
"type": "string",
"description": "NFC tag registry ID (from tascan_list_tags) — tag mode"
},
"project_id": {
"type": "string",
"description": "Project ID — page-scan mode, all lists in the project"
},
"task_list_id": {
"type": "string",
"description": "Task list ID — page-scan mode"
}
}
}Fetch the signature bundle for one signed document by signature assertion id (the signature_assertion_id inside the response value of a completed signature task). Returns the self-contained JSON an auditor can verify offline without trusting TaScan: the signed challenge preimage (v 2 also carries the typed name the signer entered, signer_typed_name), clientDataJSON, authenticatorData, signature, public key, the user-verification and backup flags, the document SHA-256, task id, completion id and time. Check it with: npx tascan-verify --signature bundle.json [--doc the-document.pdf]. Contains no signed URL, phone number or email. Read tier, organization-scoped.
| Argument | Type | Description |
|---|---|---|
| signature_assertion_id* | string | signature_assertion_id (UUID) from the completion response value of a signature task |
{
"type": "object",
"required": [
"signature_assertion_id"
],
"properties": {
"signature_assertion_id": {
"type": "string",
"description": "signature_assertion_id (UUID) from the completion response value of a signature task"
}
}
}Read one site with the projects and task lists linked to it. Read tier.
| Argument | Type | Description |
|---|---|---|
| site_id* | string | Site ID |
{
"type": "object",
"required": [
"site_id"
],
"properties": {
"site_id": {
"type": "string",
"description": "Site ID"
}
}
}Check delivery status of a previously sent TaScan SMS by its Twilio SID (returned by tascan_send_sms). Shows queued/sent/delivered/undelivered/failed plus carrier error codes.
| Argument | Type | Description |
|---|---|---|
| twilio_sid* | string | Twilio message SID (SM...) from tascan_send_sms |
{
"type": "object",
"required": [
"twilio_sid"
],
"properties": {
"twilio_sid": {
"type": "string",
"description": "Twilio message SID (SM...) from tascan_send_sms"
}
}
}Get details of a specific task including completions and subtasks. Each completion carries photo_url (raw storage path, stable) and photo_signed_url (short-lived fetchable URL, ~1h; null when no photo) so you can actually view the photo evidence. Tasks dispatched to an AI agent also carry an "agent" block (state claimed|running|completed|failed|expired|released, attempts, current run with runner/trace_id/error) — the only place agent failures are reported. A completion with status "completed" means the executor returned and its result was recorded (a model refusal, a wrong answer or an administrative note all "complete"); it does NOT mean the requested result was accepted. Acceptance is the completion evidence_check / the receipt verification.result under a named policy, and in v0.1 no policy exists for agent tasks (exact-output and rubric policies are v0.2) — check the recorded response text yourself before treating an agent completion as success (protocol §2.6, §3.2, §8.3 C11).
| Argument | Type | Description |
|---|---|---|
| task_id* | string | Task ID |
{
"type": "object",
"required": [
"task_id"
],
"properties": {
"task_id": {
"type": "string",
"description": "Task ID"
}
}
}Read a task's trail messages (protocol v0.2 trail_messages, GET /tasks/:id/messages): question, answer, handoff and note/discussion entries, last 100, newest last (reading order). Optionally filter to a comma-separated set of kinds, and with since=<ISO time> read only messages newer than the last one you saw. Read tier. Post with tascan_post_message.
| Argument | Type | Description |
|---|---|---|
| kinds | string | Optional comma-separated kind filter, e.g. question,answer,handoff,note (discussion is stored as note; both names are accepted). |
| since | string | Optional ISO 8601 time: return only messages created after it (pass the created_at of the last message you saw to read only new ones). |
| task_id* | string | Task ID (UUID). |
{
"type": "object",
"required": [
"task_id"
],
"properties": {
"kinds": {
"type": "string",
"description": "Optional comma-separated kind filter, e.g. question,answer,handoff,note (discussion is stored as note; both names are accepted)."
},
"since": {
"type": "string",
"description": "Optional ISO 8601 time: return only messages created after it (pass the created_at of the last message you saw to read only new ones)."
},
"task_id": {
"type": "string",
"description": "Task ID (UUID)."
}
}
}Read your organization's usage and quota snapshot for a billing period (GET /usage): SMS, email and AI call counts against their plan limits, the 100-receipt-per-month hard stop (used, allowance, hard_stop), the rate-limit window (60 requests per minute), and coordination-cycle spend in micro-USD for build roots created in that period. period defaults to the current UTC month (YYYY-MM). by_key additionally breaks coordination spend down per dispatch-scoped API key, refused (403 owner_required) to anything but an owner admin session -- a tsk_ key never gets it. Read tier.
| Argument | Type | Description |
|---|---|---|
| by_key | boolean | Break coordination spend down per dispatch-scoped API key -- owner admin session only. |
| period | string | Billing period as YYYY-MM (default: current UTC month). |
{
"type": "object",
"properties": {
"by_key": {
"type": "boolean",
"description": "Break coordination spend down per dispatch-scoped API key -- owner admin session only."
},
"period": {
"type": "string",
"description": "Billing period as YYYY-MM (default: current UTC month)."
}
}
}Ungated, plain read of one worker row: name, contact, org, points, streaks, timestamps. (tascan_get_worker_passport is the rich stats view; this is the boring lookup.)
| Argument | Type | Description |
|---|---|---|
| worker_id* | string | Worker ID |
{
"type": "object",
"required": [
"worker_id"
],
"properties": {
"worker_id": {
"type": "string",
"description": "Worker ID"
}
}
}Get a worker's verified work passport — task counts, lists worked, photos submitted, GPS-verified hours, points, streaks, and earned merit badges, all computed from real completion data (not self-reported). Includes the shareable profile URL.
| Argument | Type | Description |
|---|---|---|
| worker_id* | string | Worker ID |
{
"type": "object",
"required": [
"worker_id"
],
"properties": {
"worker_id": {
"type": "string",
"description": "Worker ID"
}
}
}Extend a claimed local run's lease and mark it running (POST /runs/:run_id/heartbeat -> heartbeat_agent_run). ok false with reason not_live means the run is no longer yours -- the sweep expired it, or it was never yours to begin with -- STOP, do not finish it. Never mints a completion or a receipt. Requires agent:dispatch:code. A run outside your org is 404, never 403.
| Argument | Type | Description |
|---|---|---|
| run_id* | string | The run ID from tascan_claim_run's response. |
| instance* | string | The SAME instance string you claimed with -- a mismatch answers not_live. |
| extend_seconds | integer | Optional: push expires_at out to now + this many seconds (60-86400) if that is later than the current lease -- never shortens it. |
{
"type": "object",
"required": [
"run_id",
"instance"
],
"properties": {
"run_id": {
"type": "string",
"description": "The run ID from tascan_claim_run's response."
},
"instance": {
"type": "string",
"description": "The SAME instance string you claimed with -- a mismatch answers not_live."
},
"extend_seconds": {
"type": "integer",
"description": "Optional: push expires_at out to now + this many seconds (60-86400) if that is later than the current lease -- never shortens it."
}
}
}Invite a marketplace worker to a task list — the consented intro. TaScan texts the worker from its own number ("<Your org> wants you for <list>. Reply YES to share your contact and get the list, or NO to pass."). On YES the worker appears in your org with their name + phone, receives the list link, and you get a text + a thread message. On NO or silence (7 days) you never learn who they were. Use the worker_id from tascan_search_marketplace.
| Argument | Type | Description |
|---|---|---|
| list_id* | string | Task list you want them on |
| message | string | Optional short intro prepended to the text (max 240 chars) |
| worker_id* | string | worker_id from a marketplace card |
{
"type": "object",
"required": [
"worker_id",
"list_id"
],
"properties": {
"list_id": {
"type": "string",
"description": "Task list you want them on"
},
"message": {
"type": "string",
"description": "Optional short intro prepended to the text (max 240 chars)"
},
"worker_id": {
"type": "string",
"description": "worker_id from a marketplace card"
}
}
}Link a site to a project (project_id) or to a task list shown at the site (list_id) — exactly one. Both must be in your organization. Linking twice is a no-op. Write tier.
| Argument | Type | Description |
|---|---|---|
| list_id | string | |
| site_id* | string | |
| project_id | string |
{
"type": "object",
"required": [
"site_id"
],
"properties": {
"list_id": {
"type": "string"
},
"site_id": {
"type": "string"
},
"project_id": {
"type": "string"
}
}
}List all registered AI agents with their capabilities, inbox IDs, and status. Like reading input labels on a video matrix — discover which agents are available and what they can do before dispatching work.
No arguments.
{
"type": "object",
"required": [],
"properties": {}
}List registered condition-ledger assets with their latest condition scores. Use to recover an asset_id for tascan_assess_condition or tascan_condition_history.
| Argument | Type | Description |
|---|---|---|
| project_id | string | Filter by project |
{
"type": "object",
"required": [],
"properties": {
"project_id": {
"type": "string",
"description": "Filter by project"
}
}
}List the organization's connectors (GET /integrations), e.g. the Salesforce Work Order bridge: provider, instance URL, the project and list new work lands on (with names), whether it is on, the last poll time and the last error. Never shows secrets: the client id, the client secret, its storage name and the connector config are never returned. Read tier. Turning a connector on or off, retargeting it or adding one is done by an owner or admin in the TaScan portal, not by any tool.
No arguments.
{
"type": "object",
"properties": {}
}List coordination-cycle roots in the organization (protocol v0.2, GET /coord/cycles): root id, title, status, task_type, project, build_ref, deploy_id and the T2 review id, newest first. Filter by project_id and/or status. Read tier — a reviewer's read-only key can call this too. Follow up with tascan_get_cycle_report root_id=... for the full audit trail of any row.
| Argument | Type | Description |
|---|---|---|
| limit | integer | Max rows to return (default 50, max 200). |
| status | string | Optional cycle status to filter on (e.g. active, parked, authorized, integrated) — lowercase letters and underscores only, 1-20 chars. |
| project_id | string | Optional project UUID to filter to one project's cycles. |
{
"type": "object",
"properties": {
"limit": {
"type": "integer",
"description": "Max rows to return (default 50, max 200)."
},
"status": {
"type": "string",
"description": "Optional cycle status to filter on (e.g. active, parked, authorized, integrated) — lowercase letters and underscores only, 1-20 chars."
},
"project_id": {
"type": "string",
"description": "Optional project UUID to filter to one project's cycles."
}
}
}List delegated child keys (GET /delegations): this key's own direct children (id, label, scopes, active/revoked, depth, expires_at) — never the raw credential. An admin session sees every delegated key in the org. Read tier.
No arguments.
{
"type": "object",
"properties": {}
}List registered devices (RFID portals, barcode guns, PLCs, cameras, robots, tools, sensors) in the organization. Filter by status (active/revoked) or kind; paginate with an opaque cursor. Never returns a credential hash.
| Argument | Type | Description |
|---|---|---|
| kind | string | Filter by device kind |
| limit | number | Max rows to return (1-200, default 50) |
| cursor | string | Opaque cursor from a previous response next_cursor |
| status | string | Filter by device status |
{
"type": "object",
"properties": {
"kind": {
"enum": [
"rfid_portal",
"barcode",
"nfc_reader",
"tool",
"plc",
"camera",
"robot",
"sensor",
"other"
],
"type": "string",
"description": "Filter by device kind"
},
"limit": {
"type": "number",
"description": "Max rows to return (1-200, default 50)"
},
"cursor": {
"type": "string",
"description": "Opaque cursor from a previous response next_cursor"
},
"status": {
"enum": [
"active",
"revoked"
],
"type": "string",
"description": "Filter by device status"
}
}
}List all events (task lists) within a project
| Argument | Type | Description |
|---|---|---|
| project_id* | string | Project ID |
{
"type": "object",
"required": [
"project_id"
],
"properties": {
"project_id": {
"type": "string",
"description": "Project ID"
}
}
}List marketplace invites you have sent and their status (pending / accepted / declined / expired / failed). Accepted invites include the worker's name and phone — that is the consent boundary; pending and declined never do.
| Argument | Type | Description |
|---|---|---|
| status | string |
{
"type": "object",
"required": [],
"properties": {
"status": {
"enum": [
"pending",
"accepted",
"declined",
"expired",
"failed"
],
"type": "string"
}
}
}List invoices for the org (newest first) with status, client, total, due date and share link. Filter by status (draft/sent/paid/overdue/cancelled) or project.
| Argument | Type | Description |
|---|---|---|
| status | string | |
| project_id | string |
{
"type": "object",
"required": [],
"properties": {
"status": {
"enum": [
"draft",
"sent",
"paid",
"overdue",
"cancelled"
],
"type": "string"
},
"project_id": {
"type": "string"
}
}
}List all issues for a task list (event). Returns open, acknowledged, and resolved issues with severity, type, and category. Use this to discover issues that need AI analysis via tascan_analyze_issue.
| Argument | Type | Description |
|---|---|---|
| list_id* | string | Task list (event) ID |
{
"type": "object",
"required": [
"list_id"
],
"properties": {
"list_id": {
"type": "string",
"description": "Task list (event) ID"
}
}
}List gig payments and their lifecycle status: awaiting_completion (pledged, work not verified yet), ready_to_pay (verified — pay link sent to payer), paid, canceled. Filter by task list or status.
| Argument | Type | Description |
|---|---|---|
| status | string | Filter by status |
| task_list_id | string | Filter by task list |
{
"type": "object",
"required": [],
"properties": {
"status": {
"enum": [
"awaiting_completion",
"ready_to_pay",
"paid",
"canceled"
],
"type": "string",
"description": "Filter by status"
},
"task_list_id": {
"type": "string",
"description": "Filter by task list"
}
}
}List TaScan projects in the organization (each with its number, e.g. P-00001, external_refs, report_options and its calendar_url, webcal_url, map_url and wallet_pass_url). Optional filters: status (exact, comma list), number (exact), ref (matches any external_refs[].ref exactly, e.g. a customer job number like P14-4680), and the portal's date filters: starts_after / ends_before (YYYY-MM-DD) and window (week = Sunday-Saturday, or month) on the project's start/end dates (one date = that day; none = its created day), in zone (an IANA zone; default the org time zone, else UTC).
| Argument | Type | Description |
|---|---|---|
| ref | string | Only projects whose external_refs carry this ref (exact, e.g. P14-4680) |
| zone | string | IANA time zone for the date filters, e.g. America/Los_Angeles (default: the org time zone) |
| number | string | Only the project with this project number (exact, e.g. P-00012) |
| status | string | Only projects with this status (exact; comma-separated list allowed, e.g. "active,archived") |
| window | string | Only projects overlapping this week (Sunday-Saturday) or this calendar month |
| ends_before | string | YYYY-MM-DD: only projects whose dates end before this day |
| starts_after |
List existing report links for a list or project (completion / service / project / evidence), newest first, with client acknowledgment status for service reports.
| Argument | Type | Description |
|---|---|---|
| list_id | string | |
| project_id | string |
{
"type": "object",
"required": [],
"properties": {
"list_id": {
"type": "string"
},
"project_id": {
"type": "string"
}
}
}List your org's scheduled texts (default: pending + sending, soonest first; status=sent|failed|cancelled to see history). Each row shows send_at, status, attempts, the Twilio sid once sent, and the last error for a failed row.
| Argument | Type | Description |
|---|---|---|
| status | string | Filter by status (default pending + sending) |
{
"type": "object",
"properties": {
"status": {
"enum": [
"pending",
"sending",
"sent",
"failed",
"cancelled"
],
"type": "string",
"description": "Filter by status (default pending + sending)"
}
}
}List the organization's sites (job locations: name, address, lat/lng, client company, external key). Filters: active (true, the default, false or all), q (name, address or client company) and project_id (sites linked to that project). Read tier.
| Argument | Type | Description |
|---|---|---|
| q | string | Substring of the name, address or client company |
| active | string | Default true (active sites only) |
| project_id | string | Only sites linked to this project |
{
"type": "object",
"properties": {
"q": {
"type": "string",
"description": "Substring of the name, address or client company"
},
"active": {
"enum": [
"true",
"false",
"all"
],
"type": "string",
"description": "Default true (active sites only)"
},
"project_id": {
"type": "string",
"description": "Only sites linked to this project"
}
}
}List the subtasks of a task, including completion state, stored response values, and completion timestamps (per-set timing).
| Argument | Type | Description |
|---|---|---|
| task_id* | string | Parent task ID |
{
"type": "object",
"required": [
"task_id"
],
"properties": {
"task_id": {
"type": "string",
"description": "Parent task ID"
}
}
}List all registered NFC tags in the organization with their linked projects/task lists and scan counts
No arguments.
{
"type": "object",
"properties": {}
}List the tasks in an event (task list). status "open" returns only tasks with no completion yet: use it when checking for work to do, so a long list is not re-read in full. "done" returns only completed tasks; "all" (default) returns every task.
| Argument | Type | Description |
|---|---|---|
| status | string | open = not completed yet (use this to find work), done = completed, all = everything (default). |
| list_id* | string | Task list (event) ID |
{
"type": "object",
"required": [
"list_id"
],
"properties": {
"status": {
"enum": [
"open",
"done",
"all"
],
"type": "string",
"description": "open = not completed yet (use this to find work), done = completed, all = everything (default)."
},
"list_id": {
"type": "string",
"description": "Task list (event) ID"
}
}
}List available task templates (built-in and saved)
| Argument | Type | Description |
|---|---|---|
| category | string | Filter by category (e.g. "live-events", "hospitality", "logistics") |
{
"type": "object",
"properties": {
"category": {
"type": "string",
"description": "Filter by category (e.g. \"live-events\", \"hospitality\", \"logistics\")"
}
}
}Read a task completion's verification verdicts and job queue (Verification Layer V2, GET /completions/:completion_id/verifications): completion_verifications rows (state, method, verifier, policy, confidence, finding) plus verification_jobs rows (state, attempts, last_error, result). Read tier.
| Argument | Type | Description |
|---|---|---|
| completion_id* | string | task_completions.id (UUID) — from tascan_get_task -> completions[].id. |
{
"type": "object",
"required": [
"completion_id"
],
"properties": {
"completion_id": {
"type": "string",
"description": "task_completions.id (UUID) — from tascan_get_task -> completions[].id."
}
}
}List workers (taskees) in the organization. Supports filtering by name/email/phone substring, contact-info presence, and last-activity date. Each row includes completion_count (total task completions).
| Argument | Type | Description |
|---|---|---|
| query | string | Substring match on name, email, or phone |
| has_email | boolean | true/false — same for email |
| has_phone | boolean | true = only workers with a phone on file; false = only without |
| actor_kind | string | Only these kinds of worker row: person, agent, admin or device, or a comma list (e.g. "person,agent") |
| active_since | string | ISO date/datetime — only workers with last_active_at on/after this |
| include_inactive | boolean | true = also include inactive workers (e.g. tombstones left by a merge or delete) — useful for auditing right after tascan_merge_workers / tascan_delete_worker |
{
"type": "object",
"properties": {
"query": {
"type": "string",
"description": "Substring match on name, email, or phone"
},
"has_email": {
"type": "boolean",
"description": "true/false — same for email"
},
"has_phone": {
"type": "boolean",
"description": "true = only workers with a phone on file; false = only without"
},
"actor_kind": {
"type": "string",
"description": "Only these kinds of worker row: person, agent, admin or device, or a comma list (e.g. \"person,agent\")"
},
"active_since": {
"type": "string",
"description": "ISO date/datetime — only workers with last_active_at on/after this"
},
"include_inactive": {
"type": "boolean",
"description": "true = also include inactive workers (e.g. tombstones left by a merge or delete) — useful for auditing right after tascan_merge_workers / tascan_delete_worker"
}
}
}List geofenced work zones, optionally filtered by project. Shows center, radius, routing target, and zone-lock status.
| Argument | Type | Description |
|---|---|---|
| project_id | string | Filter by project |
{
"type": "object",
"required": [],
"properties": {
"project_id": {
"type": "string",
"description": "Filter by project"
}
}
}Merge duplicate worker records into one canonical identity (Patent 4 identity consolidation). Reassigns every reference (completions, timer events, points, payments, rosters — 31 columns across 31 tables), backfills missing phone/email on the primary, sums points, and tombstones the duplicates (merged_into + is_active=false — NEVER hard-deletes). ALWAYS run with dry_run=true first and show Mike the counts; pass dry_run=false only after explicit confirmation.
| Argument | Type | Description |
|---|---|---|
| dry_run | boolean | true (default) = report reassignment counts only, change nothing. false = execute atomically. |
| primary_worker_id* | string | The canonical worker that survives (usually the one with a phone) |
| duplicate_worker_ids* | array | Worker IDs to fold into the primary (max 20) |
{
"type": "object",
"required": [
"primary_worker_id",
"duplicate_worker_ids"
],
"properties": {
"dry_run": {
"type": "boolean",
"description": "true (default) = report reassignment counts only, change nothing. false = execute atomically."
},
"primary_worker_id": {
"type": "string",
"description": "The canonical worker that survives (usually the one with a phone)"
},
"duplicate_worker_ids": {
"type": "array",
"items": {
"type": "string"
},
"description": "Worker IDs to fold into the primary (max 20)"
}
}
}Mint (or reuse) a task list's completion report and get its link (POST /lists/:id/completion-report): the same stable link tascan_generate_report type completion returns, never a worker's own shift report. Nothing is sent; text it with tascan_text_report. Write tier.
| Argument | Type | Description |
|---|---|---|
| list_id* | string |
{
"type": "object",
"required": [
"list_id"
],
"properties": {
"list_id": {
"type": "string"
}
}
}Read organization-wide analytics: view org (GET /analytics/org, the get_org_analytics rollup) or view resolutions (GET /analytics/resolutions, paginated AI issue-resolution history, filterable by severity, category, and pattern-detected). Read tier.
| Argument | Type | Description |
|---|---|---|
| view* | string | Which analytics surface to read. |
| limit | integer | resolutions only: page size (default 50, max 200). |
| offset | integer | resolutions only: page offset. |
| pattern | boolean | resolutions only: true to filter to pattern_detected rows only. |
| category | string | resolutions only: filter by issue_category. |
| severity | string | resolutions only: filter by classification_severity. |
{
"type": "object",
"required": [
"view"
],
"properties": {
"view": {
"enum": [
"org",
"resolutions"
],
"type": "string",
"description": "Which analytics surface to read."
},
"limit": {
"type": "integer",
"description": "resolutions only: page size (default 50, max 200)."
},
"offset": {
"type": "integer",
"description": "resolutions only: page offset."
},
"pattern": {
"type": "boolean",
"description": "resolutions only: true to filter to pattern_detected rows only."
},
"category": {
"type": "string",
"description": "resolutions only: filter by issue_category."
},
"severity": {
"type": "string",
"description": "resolutions only: filter by classification_severity."
}
}
}Post one evidence event into the evidence ledger (protocol v0.2 V9a, POST /evidence): an actor did an action to an object at a point in time, optionally with a location and provenance. idempotency_key is required — the same (org, idempotency_key) always returns the same row, never a second insert. Optionally correlate the event to a task or task list. Write tier; a device credential (structurally different from an API key) is not reachable through this MCP connection, so device_id is never set here.
| Argument | Type | Description |
|---|---|---|
| actor* | object | Who did it. |
| action* | string | What happened, e.g. inspected, scanned, calibrated. |
| object* | object | What it happened to. |
| location | object | Optional location. |
| provenance | object | Optional provenance object. provenance.kind first_party_scan is written only by the scan_events trigger and is refused from a caller; provenance.signature triggers device-signature verification. |
| correlation | object | Optional correlation hint. |
| occurred_at* | string | ISO timestamp for when the event actually happened. |
Post a message on a task trail (protocol v0.2 trail_messages): kind question, answer, handoff or discussion. A message never completes a task, never satisfies a gate and never pages anyone. The actor is stamped from your credential (key:<id>, actor_type "key" — a credential, never a human), never from the body; the executor and the reviewer consume a key's answers only when the key holds agent:dispatch. On an ordinary task this is write tier. On a CYCLE task (one with coord) it needs agent:dispatch (agent:dispatch:code when the task, or the asker a question task stands for, is CODE:/SHELL:) because the text can become executor prompt or reviewer input. kind=answer on a dispatcher-addressed question task answers it through coord_answer_question and releases the blocked asker; a human-addressed question is answered only on the worker page (403 here). finding (reviewer runner) and decision (human completion) cannot be posted. Body ≤ 8000 chars; idempotency_key makes a replay return the same message.
| Argument | Type | Description |
|---|---|---|
| body* | string | The message text (1-8000 chars). Treated as DATA by every reader; on a cycle task it may be prepended to the executor prompt as TRAIL INPUT. |
| kind* | string | question = a question for the record (does not block anything); answer = answers a dispatcher question task and releases the asker; handoff = hand work or context to the next agent; discussion = a note. |
| task_id* | string | Task ID (UUID). For an answer: the QUESTION task id (tascan_get_task on the asker shows coord.blocked_by). |
| reply_to | string | Optional message id this replies to. |
One call that gives a chat client (ChatGPT, Claude) a whole TaScan project in about 2,000 words: the project, every task list with task counts, open decisions and questions, the last 5 coordination cycles with verdicts and spend, the latest 5 receipts, and total spend — returned as Markdown (capped at 12,000 chars). Read tier. Use it before asking a human to paste anything.
| Argument | Type | Description |
|---|---|---|
| project_id* | string | Project ID (UUID). |
{
"type": "object",
"required": [
"project_id"
],
"properties": {
"project_id": {
"type": "string",
"description": "Project ID (UUID)."
}
}
}A project's progress and insights (GET /projects/:id/insights), counted like the portal's panel: per task list done of total (people's tasks), open issues by list (open / acknowledged / in progress, 30 days; an archived, completed or demo project counts none unless demo=true), and the newest 20 activity items (completions, scans, issues) with who and when. Read tier.
| Argument | Type | Description |
|---|---|---|
| demo | boolean | Count a demo / test project's open issues too (default false) |
| project_id* | string |
{
"type": "object",
"required": [
"project_id"
],
"properties": {
"demo": {
"type": "boolean",
"description": "Count a demo / test project's open issues too (default false)"
},
"project_id": {
"type": "string"
}
}
}Query one task's submitted responses across every list in a project — e.g. the same exercise repeated across many workout lists returns one chronological progression series instead of N report lookups. Match by task title pattern or exact task ID. Subtask completions interleave into the same series labeled 'Task › Subtask' (e.g. per-set values Set 1/2/3 with their own timestamps), so set-level progression chains across lists automatically.
| Argument | Type | Description |
|---|---|---|
| task* | string | Task title pattern (case-insensitive substring) or exact task ID |
| limit | number | Max responses to return (default 200, max 500) |
| project_id* | string | Project ID |
{
"type": "object",
"required": [
"project_id",
"task"
],
"properties": {
"task": {
"type": "string",
"description": "Task title pattern (case-insensitive substring) or exact task ID"
},
"limit": {
"type": "number",
"description": "Max responses to return (default 200, max 500)"
},
"project_id": {
"type": "string",
"description": "Project ID"
}
}
}One action to a genuinely verified receipt (POST /quickstart). Under your org it reuses-or-creates the project "TaScan Quickstart", its list "Quickstart" and the worker "Quickstart executor", adds a NEW task "Reply with the exact text: <expected>", completes it with your key, has the named policy quickstart_exact_output v1 compare the response to the expected text (verified or refuted, signed into the receipt), and publishes the receipt's public profile. expected defaults to "VERIFIED" (max 200 chars); response defaults to expected — pass a different response to see a refuted receipt. Returns the receipt URL and the public verify URL. Write tier.
| Argument | Type | Description |
|---|---|---|
| expected | string | The exact text the task asks for (default "VERIFIED", max 200 chars). |
| response | string | The response to submit (default: the expected text). A different value is refuted. |
{
"type": "object",
"properties": {
"expected": {
"type": "string",
"description": "The exact text the task asks for (default \"VERIFIED\", max 200 chars)."
},
"response": {
"type": "string",
"description": "The response to submit (default: the expected text). A different value is refuted."
}
}
}Step 2 of the Closed-Loop Autonomous Operations Protocol. Post an AI-generated recommendation to an issue thread. Accepts both a text recommendation and an optional structured_recommendation object with task definitions for auto-dispatch. The recommendation is persisted in the AI audit trail.
| Argument | Type | Description |
|---|---|---|
| ai_agent | string | Name of the AI agent posting (default: TaScan AI) |
| issue_id* | string | Issue ID to recommend a fix for |
| recommendation* | string | The AI-generated recommendation text (clear, actionable instructions) |
| structured_recommendation | object | Optional structured recommendation with tasks for auto-dispatch. Format: { recommendation_summary, confidence_score, tasks: [{ title, description, response_type, requires_photo, is_safety_checkpoint, sort_order }], estimated_duration_minutes, required_responder_role } |
{
"type": "object",
"required": [
"issue_id",
"recommendation"
],
"properties": {
"ai_agent": {
"type": "string",
"description": "Name of the AI agent posting (default: TaScan AI)"
},
"issue_id": {
"type": "string",
"description": "Issue ID to recommend a fix for"
},
"recommendation": {
"type": "string",
"description": "The AI-generated recommendation text (clear, actionable instructions)"
},
"structured_recommendation": {
"type": "object",
"description": "Optional structured recommendation with tasks for auto-dispatch. Format: { recommendation_summary, confidence_score, tasks: [{ title, description, response_type, requires_photo, is_safety_checkpoint, sort_order }], estimated_duration_minutes, required_responder_role }"
}
}
}D4: the dispatcher records the Netlify deploy id for an authorized cycle — Integrate cards stop landing on Mike for something a key can prove instead (protocol v0.2, migration 181, coord_record_integration). Requires agent:dispatch. The root must be `authorized` (its checkpoint already Approved) with an open Integrate task; deploy_id must be a real Netlify deploy id — 24 lowercase hex, optionally "deployed:<id>" and/or a trailing build-ref hex prefix ("Deploy" and anything else is refused, bad_deploy_id). On success the root flips to `integrated`, the existing "Integrated: deploy …" trail note is posted, and the completion is stamped with your key as the actor (source api:key:<id>) — never as a human on the page. Idempotent: replaying the SAME deploy_id after the root is already integrated returns the same completion (replayed:true); a DIFFERENT deploy_id after integration is refused (409) without changing anything. A human may still complete the Integrate card on the worker page as an ops fallback, but only with a real deploy id too.
| Argument | Type | Description |
|---|---|---|
| note | string | Optional note (≤ 2000 chars) recorded alongside the completion — e.g. what changed in this deploy. |
| root_id* | string | The cycle root = the T1 build task id (tascan_get_cycle_report / tascan_create_cycle). Must currently be authorized (checkpoint Approved, Integrate task open). |
| deploy_id* | string | The Netlify deploy id: 24 lowercase hex, optionally prefixed "deployed:" and/or followed by a space and a ≥12-hex prefix of the build_ref. |
{
"type": "object",
"required": [
"root_id",
"deploy_id"
],
"properties": {
"note": {
"type": "string",
"description": "Optional note (≤ 2000 chars) recorded alongside the completion — e.g. what changed in this deploy."
},
"root_id": {
"type": "string",
"description": "The cycle root = the T1 build task id (tascan_get_cycle_report / tascan_create_cycle). Must currently be authorized (checkpoint Approved, Integrate task open)."
},
"deploy_id": {
"type": "string",
"description": "The Netlify deploy id: 24 lowercase hex, optionally prefixed \"deployed:\" and/or followed by a space and a ≥12-hex prefix of the build_ref."
}
}
}Register a new AI agent in the agent registry. The agent will appear in tascan_list_agents and can receive dispatched tasks. Self-registration for AI agents joining the TaScan network. REQUIRES the agent:dispatch permission (defining a dispatch target is a dispatch permission); inbox_id must be a task list (event) in your organization.
| Argument | Type | Description |
|---|---|---|
| id* | string | Unique agent ID (e.g. "my-agent-1") |
| name* | string | Display name (e.g. "Research Bot") |
| type* | string | Agent type |
| model | string | Model powering this agent (e.g. "claude-sonnet-4-6") |
| inbox_id* | string | Task list ID this agent monitors for new tasks |
| location | string | Where the agent runs (e.g. "AWS us-east-1") |
| worker_id | string | TaScan worker ID for this agent |
| description | string | What this agent does |
| capabilities* |
Register a physical asset (equipment, structure, vehicle, machine) in the condition ledger so it can be assessed over time. Each asset gets a longitudinal condition history with AI scoring and degradation trajectory.
| Argument | Type | Description |
|---|---|---|
| name* | string | Asset name (e.g. "LED Wall Processor #3") |
| asset_type | string | Type (e.g. "LED processor", "forklift", "scaffold") |
| project_id | string | |
| description | string | Context the AI assessor should know |
| serial_number | string | Serial number or asset tag (unique per org) |
| location_description | string |
{
"type": "object",
"required": [
"name"
],
"properties": {
"name": {
"type": "string",
"description": "Asset name (e.g. \"LED Wall Processor #3\")"
},
"asset_type": {
"type": "string",
"description": "Type (e.g. \"LED processor\", \"forklift\", \"scaffold\")"
},
"project_id": {
"type": "string"
},
"description": {
"type": "string",
"description": "Context the AI assessor should know"
},
"serial_number": {
"type": "string",
"description": "Serial number or asset tag (unique per org)"
},
"location_description": {
"type": "string"
}
}
}Register a physical NFC tag to a project, task list, or specific task. When someone taps the tag, TaScan routes them to the linked resource. Tags use NTAG215 chips and are programmed with NFC Tools Pro.
| Argument | Type | Description |
|---|---|---|
| task_id | string | Task ID (required for task targets) |
| tag_name* | string | Friendly name (e.g., "Ballroom A Door", "Breaker Panel 3") |
| project_id | string | Project ID |
| target_type* | string | What this tag points to |
| task_list_id | string | Task list ID (required for task_list/task targets) |
| tag_hardware_id* | string | NFC tag hardware serial number (e.g., "04:CB:6C:51:CE:2A:81") |
| location_description | string | Physical location of the tag |
{
"type": "object",
"required": [
"tag_hardware_id",
"tag_name",
"target_type"
],
"properties": {
"task_id": {
"type": "string",
"description": "Task ID (required for task targets)"
},
"tag_name": {
"type": "string",
"description": "Friendly name (e.g., \"Ballroom A Door\", \"Breaker Panel 3\")"
},
"project_id": {
"type": "string",
"description": "Project ID"
},
"target_type": {
"enum": [
"project",
"task_list",
"task"
],
"type": "string",
"description": "What this tag points to"
},
"task_list_id": {
"type": "string",
"description": "Task list ID (required for task_list/task targets)"
},
"tag_hardware_id": {
"type": "string",
"description": "NFC tag hardware serial number (e.g., \"04:CB:6C:51:CE:2A:81\")"
},
"location_description": {
"type": "string",
"description": "Physical location of the tag"
}
}
}Reply to a task list WITH a task list — the two-way tasking primitive. Creates a new list linked into the parent's thread, aimed back at whoever sent the original (e.g. "Grant access — pick a window" with response_type date, or an info request with response_type text). The org gets pinged; the thread shows in both the worker portal and Simple Mode. Use tascan_get_thread-style follow-up via tascan_list_projects/tascan_get_report to read answers.
| Argument | Type | Description |
|---|---|---|
| tasks* | array | Items the recipient answers — each becomes a typed task |
| title* | string | Reply list title (e.g. "Before I can start...") |
| author_name | string | Who is replying (shown in the thread) |
| parent_list_id* | string | The list being replied to |
{
"type": "object",
"required": [
"parent_list_id",
"title",
"tasks"
],
"properties": {
"tasks": {
"type": "array",
"items": {
"type": "object",
"required": [
"title"
],
"properties": {
"title": {
"type": "string"
},
"description": {
"type": "string"
},
"response_type": {
"enum": [
"checkbox",
"text",
"number",
"date",
"choice",
"photo",
"link",
"url",
"file"
],
"type": "string",
"description": "\"date\" for scheduling windows, \"text\" for info requests, \"choice\" needs response_config.options. \"link\" = the worker submits a URL. \"url\" = the worker visits a preset URL and confirms. \"file\" = the worker uploads a file."
},
"response_config": {
"type": "object"
}
}
},
"description": "Items the recipient answers — each becomes a typed task"
},
"title": {
"type": "string",
"description": "Reply list title (e.g. \"Before I can start...\")"
},
"author_name": {
"type": "string",
"description": "Who is replying (shown in the thread)"
},
"parent_list_id": {
"type": "string",
"description": "The list being replied to"
}
}
}A report's engagement (GET /reports/:token/engagement, get_report_engagement): declared, engaged and idle minutes per worker shift on the report's list. There are no view or open counts. Read tier.
| Argument | Type | Description |
|---|---|---|
| token* | string | The report token |
{
"type": "object",
"required": [
"token"
],
"properties": {
"token": {
"type": "string",
"description": "The report token"
}
}
}Pledge a payment on a task list: when the list is verified complete (every task done + photo evidence on photo-required tasks), the payer automatically receives a Stripe pay link that routes the money DIRECTLY to the worker (0% TaScan fee). No money moves and no card is stored at pledge time. The worker must have completed payout onboarding (Get Paid on their profile).
| Argument | Type | Description |
|---|---|---|
| memo | string | What the payment is for (shown to the payer) |
| worker_id* | string | Worker who gets paid |
| payer_name | string | |
| payer_email* | string | Who pays — receives the pay link on verification |
| amount_cents* | number | Amount in cents ($1 min, $10,000 max) |
| task_list_id* | string | Task list the payment is tied to |
| idempotency_key | string | B1: optional replay key (up to 1024 chars, no braces) sent verbatim as the Idempotency-Key header instead of a fresh one per call — a retried call with the SAME key replays the original pledge instead of creating a second one. |
Enqueue an autonomous verification job for a task completion (Verification Layer V2 + V7 doc_check, POST /completions/:completion_id/verification-jobs): http_probe checks a URL (params url, and optionally expect_status, expect_content_type, expect_sha256), doc_check runs a named policy against params.url and params.policy_id. Read the result with tascan_list_verifications once the job runs. Write tier.
| Argument | Type | Description |
|---|---|---|
| params* | object | For http_probe: url, and optionally expect_status, expect_content_type, expect_sha256. For doc_check: url and policy_id, where policy_id names an active doc_check policy. |
| run_at | string | Optional ISO timestamp to schedule the job for (default now). |
| check_type* | string | Which autonomous check to run. |
| completion_id* | string | task_completions.id (UUID) — from tascan_get_task -> completions[].id. |
| idempotency_key | string | Optional replay key (up to 200 chars). |
{
"type": "object",
"required": [
"completion_id",
"check_type",
"params"
],
"properties": {
"params": {
"type": "object",
"description": "For http_probe: url, and optionally expect_status, expect_content_type, expect_sha256. For doc_check: url and policy_id, where policy_id names an active doc_check policy."
},
"run_at": {
"type": "string",
"description": "Optional ISO timestamp to schedule the job for (default now)."
},
"check_type": {
"enum": [
"http_probe",
"doc_check"
],
"type": "string",
"description": "Which autonomous check to run."
},
"completion_id": {
"type": "string",
"description": "task_completions.id (UUID) — from tascan_get_task -> completions[].id."
},
"idempotency_key": {
"type": "string",
"description": "Optional replay key (up to 200 chars)."
}
}
}Revoke a delegated child key immediately (DELETE /delegations/:key_id) — self-revoke, revoke by an ancestor key, or by an admin session of the org; any other caller is refused. Revoking cascades to every key IT delegated, in turn (the database trigger, not application code). Idempotent: revoking an already-revoked key reports already_revoked, no error.
| Argument | Type | Description |
|---|---|---|
| key_id* | string | The delegated key id to revoke (from tascan_list_delegations) |
{
"type": "object",
"required": [
"key_id"
],
"properties": {
"key_id": {
"type": "string",
"description": "The delegated key id to revoke (from tascan_list_delegations)"
}
}
}Revoke a device credential immediately — the kill switch for a lost, stolen, or decommissioned device (RFID portal, barcode gun, PLC, camera, robot, tool, sensor). Its next evidence post is refused. Idempotent: revoking an already-revoked device reports already_revoked and mints no second receipt. Requires the device:admin permission (owner-only, needs full, never reachable through OAuth).
| Argument | Type | Description |
|---|---|---|
| reason | string | Why this device is being revoked (up to 500 characters) |
| device_id* | string | Device ID to revoke |
{
"type": "object",
"required": [
"device_id"
],
"properties": {
"reason": {
"type": "string",
"description": "Why this device is being revoked (up to 500 characters)"
},
"device_id": {
"type": "string",
"description": "Device ID to revoke"
}
}
}Schedule a transactional TaScan SMS for a future time (up to 90 days out): the text is sent by TaScan's own scheduler (every 5 minutes) through the same guarded lane as tascan_send_sms — recipient must be a worker of your org or a phone the org already knows, STOP opt-outs honoured, burst limits and the SMS quota apply, the "TaScan:" prefix is added, and a list_id appends a tap-to-open checklist link. Use this for reminders (e.g. "log your out time" each show night) — nothing outside TaScan needs to stay awake. Returns the scheduled row id; cancel with tascan_cancel_scheduled_sms while it is still pending. The same idempotency_key within an org returns the existing row instead of a duplicate.
| Argument | Type | Description |
|---|---|---|
| phone | string | Raw phone (E.164) — must already belong to a worker or roster entry of your org |
| list_id | string | Optional task list ID — appends the tap-to-open checklist link |
| message* | string | Message text (1-400 chars, links stripped). Transactional and work-related only. |
| send_at* | string | When to send — ISO 8601 with a timezone offset, e.g. "2026-09-25T18:30:00-07:00" (Las Vegas is -07:00 in September). Delivery happens on the next 5-minute tick at or after this time. |
| worker_id | string | Worker ID — texts their phone on file (preferred over raw phone) |
| include_link | boolean | Append the list link (default true when list_id is given) |
Search the cross-org Worker Marketplace: workers who opted in (discoverable=true on their passport), ranked by passkey trust tier + verified completion volume. Skills are AI-inferred from REAL completed work, not resumes — each carries a verified_task_count and a civilian_equivalent job title. Returns sanitized public cards only (first name + last initial, skills, stats, passport URL) — never phone, email, or org membership.
| Argument | Type | Description |
|---|---|---|
| q | string | Skill, category, name, or civilian job title — e.g. "forklift", "LED wall", "AV technician" |
| city | string | Filter by the worker's opt-in home city, e.g. "Las Vegas" |
| limit | number | Max cards (default 25, max 50) |
| available | boolean | Only workers who marked themselves available on their passport |
| min_completions | number | Only workers with at least this many verified completions |
{
"type": "object",
"required": [],
"properties": {
"q": {
"type": "string",
"description": "Skill, category, name, or civilian job title — e.g. \"forklift\", \"LED wall\", \"AV technician\""
},
"city": {
"type": "string",
"description": "Filter by the worker's opt-in home city, e.g. \"Las Vegas\""
},
"limit": {
"type": "number",
"description": "Max cards (default 25, max 50)"
},
"available": {
"type": "boolean",
"description": "Only workers who marked themselves available on their passport"
},
"min_completions": {
"type": "number",
"description": "Only workers with at least this many verified completions"
}
}
}Send a transactional TaScan SMS text to a worker (by worker_id, using their phone on file) or to a raw phone number. Optionally attach a task list — the recipient gets a tap-to-open checklist link. Attach images (MMS) with media_url (public https image URL) or media_base64 + media_type (jpeg/png/gif, max 3.5 MB) — e.g. a diagram, a tutorial photo or a site photo; the message text is optional when an image is attached. Sends from TaScan's carrier-registered A2P number (or the org's own Twilio if BYOK). Counts against the org's monthly SMS quota unless BYOK. Messages are auto-prefixed with "TaScan:" per carrier registration; transactional/work-related content only, no marketing.
| Argument | Type | Description |
|---|---|---|
| phone | string | Raw phone number (e.g. "+17025551234") — used when no worker_id given |
| list_id | string | Optional task list ID — appends a tap-to-open worker checklist link |
| message | string | Message text. Transactional and work-related only. |
| media_url | string | Optional image to attach (MMS): a public https URL of a JPEG, PNG or GIF. The carrier fetches it at send time. |
| worker_id | string | Worker ID — sends to their phone on file (preferred over raw phone) |
| media_type | string | MIME type of media_base64 (not needed when media_base64 is a data: URI). |
| include_link | boolean |
Send a branded TaScan task notification email via SendGrid. Can notify anyone about a specific task list or task. Includes QR code, task summary, and "Open in TaScan" button.
| Argument | Type | Description |
|---|---|---|
| list_id* | string | Task list (event) ID |
| message | string | Optional custom message to include in the email body |
| subject | string | Custom email subject (defaults to auto-generated) |
| task_id | string | Optional specific task ID to highlight |
| to_name | string | Recipient display name |
| to_email* | string | Recipient email address |
| include_qr | boolean | Include QR code for the task list in the email (default: true) |
{
"type": "object",
"required": [
"to_email",
"list_id"
],
"properties": {
"list_id": {
"type": "string",
"description": "Task list (event) ID"
},
"message": {
"type": "string",
"description": "Optional custom message to include in the email body"
},
"subject": {
"type": "string",
"description": "Custom email subject (defaults to auto-generated)"
},
"task_id": {
"type": "string",
"description": "Optional specific task ID to highlight"
},
"to_name": {
"type": "string",
"description": "Recipient display name"
},
"to_email": {
"type": "string",
"description": "Recipient email address"
},
"include_qr": {
"type": "boolean",
"description": "Include QR code for the task list in the email (default: true)"
}
}
}Identify exactly which TaScan server and schema this MCP session is talking to. Call this FIRST when diagnosing anything — it makes "dev server masquerading as production" and "is my fix deployed yet" one tool call instead of an inference.
No arguments.
{
"type": "object",
"required": [],
"properties": {}
}Get a task list's share link and its QR code (an SVG drawn by TaScan; no third-party QR service), and optionally send the link to one person: send.phone or send.worker_id texts it through TaScan's guarded SMS lane (recipient must be known to your org, STOP opt-outs and limits apply), send.email e-mails it. Write tier for the link; sending needs the full access level. style is reserved for saved QR styles and ignored today.
| Argument | Type | Description |
|---|---|---|
| send | object | Optional: send the link to ONE recipient — { phone } or { worker_id } (text) or { email }, plus an optional message (at most 300 characters) |
| style | string | Reserved for saved QR styles (SR-P4c): accepted and ignored today |
| list_id* | string | Task list (event) ID |
{
"type": "object",
"required": [
"list_id"
],
"properties": {
"send": {
"type": "object",
"properties": {
"email": {
"type": "string"
},
"phone": {
"type": "string"
},
"message": {
"type": "string"
},
"worker_id": {
"type": "string"
}
},
"description": "Optional: send the link to ONE recipient — { phone } or { worker_id } (text) or { email }, plus an optional message (at most 300 characters)"
},
"style": {
"type": "string",
"description": "Reserved for saved QR styles (SR-P4c): accepted and ignored today"
},
"list_id": {
"type": "string",
"description": "Task list (event) ID"
}
}
}Text an existing report's link to a phone (POST /reports/:token/text) through the guarded SMS lane: the number must belong to a worker or roster entry of your org, burst limits, the SMS quota and STOP opt-outs apply, and the report's task list proves ownership. token is the report token (tascan_mint_completion_report / tascan_generate_report). Full tier (it sends to a person).
| Argument | Type | Description |
|---|---|---|
| note | string | Optional short intro, at most 160 characters |
| phone* | string | E.164 or 10-digit US number |
| token* | string | The report token |
{
"type": "object",
"required": [
"token",
"phone"
],
"properties": {
"note": {
"type": "string",
"description": "Optional short intro, at most 160 characters"
},
"phone": {
"type": "string",
"description": "E.164 or 10-digit US number"
},
"token": {
"type": "string",
"description": "The report token"
}
}
}The organization's Today summary (GET /today), counted exactly like the portal's Today: due_today, closed_today, awaiting_check, retake, photos_verified_7d and the needs list (each item's first reason of retake, issue, review, overdue, due_today). Archived, completed and demo projects never count (demo only with demo=true). The day is in zone (an IANA zone), else the org time zone, else UTC (timezone_fallback true). Read tier.
| Argument | Type | Description |
|---|---|---|
| demo | boolean | Count demo / test projects too (default false) |
| zone | string | IANA time zone for the day boundary, e.g. America/Los_Angeles (default: the org time zone) |
{
"type": "object",
"properties": {
"demo": {
"type": "boolean",
"description": "Count demo / test projects too (default false)"
},
"zone": {
"type": "string",
"description": "IANA time zone for the day boundary, e.g. America/Los_Angeles (default: the org time zone)"
}
}
}Remove a site's link to a project (project_id) or to a task list (list_id) — exactly one. The site, the project and the list themselves are not changed. Write tier.
| Argument | Type | Description |
|---|---|---|
| list_id | string | |
| site_id* | string | |
| project_id | string |
{
"type": "object",
"required": [
"site_id"
],
"properties": {
"list_id": {
"type": "string"
},
"site_id": {
"type": "string"
},
"project_id": {
"type": "string"
}
}
}Update an event / task list (name, description, team_mode, multi_instance, timer_mode). team_mode and multi_instance cannot both be true.
| Argument | Type | Description |
|---|---|---|
| name | string | New name |
| list_id* | string | Task list (event) ID |
| team_mode | boolean | Team mode — shared completions |
| timer_mode | string | Timer mode (auto or manual) |
| description | string | New description |
| multi_instance | boolean | Multi-instance — each worker gets isolated copy |
| assigned_worker_ids | array | Worker UUIDs to assign to this event (additive — workers not named are left alone). Each gets a personal tap-to-open link. Note: a worker holds one event assignment per project — assigning moves them. |
{
"type": "object",
"required": [
"list_id"
],
"properties": {
"name": {
"type": "string",
"description": "New name"
},
"list_id": {
"type": "string",
"description": "Task list (event) ID"
},
"team_mode": {
"type": "boolean",
"description": "Team mode — shared completions"
},
"timer_mode": {
"type": "string",
"description": "Timer mode (auto or manual)"
},
"description": {
"type": "string",
"description": "New description"
},
"multi_instance": {
"type": "boolean",
"description": "Multi-instance — each worker gets isolated copy"
},
"assigned_worker_ids": {
"type": "array",
"items": {
"type": "string"
},
"description": "Worker UUIDs to assign to this event (additive — workers not named are left alone). Each gets a personal tap-to-open link. Note: a worker holds one event assignment per project — assigning moves them."
}
}
}Update an invoice: mark it paid (records paid_at; a receipted ledger event with the actor, invoice id, number, amount and the before/after status is written first, and nothing changes if it cannot be), overdue, cancelled, or edit client details / notes / due date. A paid invoice is final: any further change is refused 409 invoice_paid_locked (no refunds, edits or deletes).
| Argument | Type | Description |
|---|---|---|
| notes | string | |
| status | string | |
| paid_at | string | ISO timestamp (default now) when status = paid |
| due_date | string | |
| invoice_id* | string | |
| client_name | string | |
| client_email | string | |
| client_phone | string |
{
"type": "object",
"required": [
"invoice_id"
],
"properties": {
"notes": {
"type": "string"
},
"status": {
"enum": [
"draft",
"sent",
"paid",
"overdue",
"cancelled"
],
"type": "string"
},
"paid_at": {
"type": "string",
"description": "ISO timestamp (default now) when status = paid"
},
"due_date": {
"type": "string"
},
"invoice_id": {
"type": "string"
},
"client_name": {
"type": "string"
},
"client_email": {
"type": "string"
},
"client_phone": {
"type": "string"
}
}
}Update a project (name, location, status, dates, number, external_refs, labor_rules, report_options). A number already used in the org is refused 409; external_refs replaces the whole list. report_options is merged into the stored object (other keys kept, a key set to null removed). labor_rules is validated like the projects CHECK.
| Argument | Type | Description |
|---|---|---|
| name | string | New name |
| number | string | New project number, 1-40 letters, digits, dot, underscore or hyphen, unique in the org. A P-<digits> number is refused 400 unless it is the project's current number |
| status | string | Status |
| end_date | string | End date (ISO) |
| location | string | New location |
| project_id* | string | Project ID |
| start_date | string | Start date (ISO) |
| labor_rules | object | An object (or null to clear): { ot_after_hours (> 0, <= 24, or null), dt_after_hours (> ot_after_hours, or > 10 when that is null, <= 24, or null) } |
| external_refs |
Update a site (name, address, lat + lng, client_company, external_key, is_active). is_active false deactivates it (the soft delete: its links, work orders and visits stay). Write tier.
| Argument | Type | Description |
|---|---|---|
| lat | number | |
| lng | number | |
| name | string | |
| address | string | |
| site_id* | string | Site ID |
| is_active | boolean | |
| external_key | string | |
| client_company | string |
{
"type": "object",
"required": [
"site_id"
],
"properties": {
"lat": {
"type": "number"
},
"lng": {
"type": "number"
},
"name": {
"type": "string"
},
"address": {
"type": "string"
},
"site_id": {
"type": "string",
"description": "Site ID"
},
"is_active": {
"type": "boolean"
},
"external_key": {
"type": "string"
},
"client_company": {
"type": "string"
}
}
}Update a subtask (title, description, response_type, response_config, requires_photo, sort_order).
| Argument | Type | Description |
|---|---|---|
| title | string | |
| sort_order | number | |
| subtask_id* | string | Subtask ID |
| description | string | |
| response_type | string | "link" = the worker submits a URL. "url" = the worker visits a preset URL and confirms. "file" = the worker uploads a file. |
| requires_photo | boolean | |
| response_config | object |
{
"type": "object",
"required": [
"subtask_id"
],
"properties": {
"title": {
"type": "string"
},
"sort_order": {
"type": "number"
},
"subtask_id": {
"type": "string",
"description": "Subtask ID"
},
"description": {
"type": "string"
},
"response_type": {
"enum": [
"checkbox",
"number",
"text",
"choice",
"link",
"url",
"file"
],
"type": "string",
"description": "\"link\" = the worker submits a URL. \"url\" = the worker visits a preset URL and confirms. \"file\" = the worker uploads a file."
},
"requires_photo": {
"type": "boolean"
},
"response_config": {
"type": "object"
}
}
}Update a task (title, description, response_type, flags, sort_order). A task that sits in an AI agent inbox is agent input (the runner executes title + description), so ANY edit to it needs the agent:dispatch permission — agent:dispatch:code when the task is or becomes CODE:/SHELL:.
| Argument | Type | Description |
|---|---|---|
| title | string | New title |
| task_id* | string | Task ID |
| sort_order | number | Sort position |
| assigned_to | string | Worker to assign this task to — pass the worker UUID (validated against your org and stored as the worker's name, which is how the worker portal matches "my tasks"). A plain name string is also accepted as-is. Pass null to clear. |
| description | string | New description |
| response_type | string | See tascan_add_tasks for guidance. "text" for info collection, "photo" for visual proof, "checkbox" for yes/no only, "link" for the worker submitting a URL, "url" for the worker visiting a preset URL and confirming, "file" for the worker uploading a file. |
| requires_photo | boolean | Require photo |
| response_config | object |
Update a worker profile (name, phone, email, actor_kind). Changing actor_kind to or from agent or admin is refused for every key: only an org owner or admin, signed in to the portal, can do that.
| Argument | Type | Description |
|---|---|---|
| name | string | New name |
| string | New email | |
| phone | string | New phone |
| worker_id* | string | Worker ID |
| actor_kind | string | What this worker row stands for (to or from agent / admin: owner or admin session only) |
{
"type": "object",
"required": [
"worker_id"
],
"properties": {
"name": {
"type": "string",
"description": "New name"
},
"email": {
"type": "string",
"description": "New email"
},
"phone": {
"type": "string",
"description": "New phone"
},
"worker_id": {
"type": "string",
"description": "Worker ID"
},
"actor_kind": {
"enum": [
"person",
"agent",
"admin",
"device"
],
"type": "string",
"description": "What this worker row stands for (to or from agent / admin: owner or admin session only)"
}
}
}Update a geofenced zone — move the center, resize the radius, change the routing target, toggle zone-lock, or deactivate it (is_active=false).
| Argument | Type | Description |
|---|---|---|
| lat | number | |
| lng | number | |
| kind | string | |
| name | string | |
| polygon | array | Replace geometry with a polygon ([[lat,lng],...], ≥3 vertices); pass null to revert to a circle |
| zone_id* | string | Zone ID |
| radius_m | number | |
| is_active | boolean | |
| notify_email | string | |
| required_ppe | array | |
| task_list_id | string | |
Independently verify a TaScan Action Receipt JWS (protocol 6.8, POST /receipts/verify): size, envelope, signature, issuer origin, key lifecycle, hashing profile, value semantics, schema and chain check, with an online issued/serial check for the reference issuer. Public route: no TaScan scope is required by the API itself, though this MCP connection still needs some valid key to place any tools/call. Works for both the full and the public export profile. Keys come only from the issuer's own well-known key document (https://app.tascan.io/.well-known/tascan-receipt-keys.json for the reference issuer, or the equivalent well-known path for a foreign one) — this tool never supplies keys itself. Never verifies locally — always calls the reference verifier.
| Argument | Type | Description |
|---|---|---|
| jws* | string | The compact JWS to verify (from tascan_get_receipt's output, or any TaScan-format receipt). |
{
"type": "object",
"required": [
"jws"
],
"properties": {
"jws": {
"type": "string",
"description": "The compact JWS to verify (from tascan_get_receipt's output, or any TaScan-format receipt)."
}
}
}Hazard-zone compliance audit (OSHA / insurance): every zone crossing, PPE checkpoint verdict (complied / failed with what was missing / skipped), and breach, plus injury reports cross-referenced with the worker's last PPE checkpoint before the injury. Scope by project or zone, optionally by worker and date range. Same rows the printable Evidence Pack shows.
| Argument | Type | Description |
|---|---|---|
| since | string | ISO date/time lower bound |
| until | string | ISO date/time upper bound |
| zone_id | string | |
| worker_id | string | |
| project_id | string |
{
"type": "object",
"required": [],
"properties": {
"since": {
"type": "string",
"description": "ISO date/time lower bound"
},
"until": {
"type": "string",
"description": "ISO date/time upper bound"
},
"zone_id": {
"type": "string"
},
"worker_id": {
"type": "string"
},
"project_id": {
"type": "string"
}
}
}{
"type": "object",
"required": [
"fanout_id",
"action"
],
"properties": {
"action": {
"enum": [
"pause",
"resume",
"cancel",
"close_barrier",
"retry_synth"
],
"type": "string",
"description": "pause, resume, cancel, close_barrier or retry_synth. No amend, no answer (phase 1)."
},
"reason": {
"type": "string",
"description": "Optional free-text reason (<= 4000 chars), recorded on the control child's completion."
},
"fanout_id": {
"type": "string",
"description": "Fan-out id (UUID) — the T_F root."
},
"exclude_seats": {
"type": "array",
"items": {
"type": "integer"
},
"description": "close_barrier only: 1-based seat numbers to drop from the barrier instead of waiting for them to settle."
},
"idempotency_key": {
"type": "string",
"description": "Optional replay key (<= 200 chars). The same key returns the prior outcome (replayed:true) and applies nothing a second time."
}
}
}| Type |
|---|
| Description |
|---|
| kind | string | Cycle kind (default build). review_only = no model builds anything: T1 is "CAPTURE: <title>", a local-executor task that stores artifact_paths from the repo at HEAD as the bundle (same build_ref, size cap and scope check as a CODE build) and completes in seconds; T2 REVIEW then reviews that bundle. Use it to get an independent review of documents or of code already committed by hand. Stored as coord.kind_of_cycle on the root. |
| repo | string | Which codebase on the executor the build runs in — an alias from the executor's allowlist (tascan-agent/repos.json), e.g. "tascan" (default), "merchskipper", "inkskipper", "rangerlizzy", "cardvault", "take1", "fitcheck", "safegrid", "eternity", "maniacs". artifact_paths are relative to that repo. An unknown alias is refused by the executor (repo_unknown) and the cycle parks. |
| title* | string | Short human title (1-200 chars). T1 becomes "CODE: <title>" ("CAPTURE: <title>" when kind=review_only), T2 "REVIEW: <title>". |
| dry_run | boolean | true = PREFLIGHT only: the API validates the whole dispatch and returns every problem at once (code, path, detail) plus the unchecked[] list (what only the executor / the RPC can decide) and warnings[] — nothing is queued, no idempotency key is consumed. Recommended before every real create. Default false. |
| reviews | array | Optional multi-lens review panel (design item 14a) instead of today's single OpenAI review_brief lens — 1-8 entries, each: {lens: slug matching ^[a-z][a-z0-9-]{0,39}$ unique per array (e.g. "code-correctness", "security", "ui-ux"), brief: 1-8000 chars of cycle-specific context for that lens, provider: "openai"|"anthropic"|"gemini", model?: 1-100 chars, blocking?: boolean (default true — at least one entry must be blocking or the whole array is refused), max_tool_calls?: integer (openai only)}. One REVIEW: task mints per lens, all blocked on T1; the Decision task mints only once every blocking lens has approved the same build. Omit for today's single OpenAI code review. |
| task_type | string | T1 prefix (default CODE). CAPTURE is never caller-settable: kind=review_only makes T1 "CAPTURE:" itself. With kind=review_only the value is sent as given — CODE is accepted (T1 is still CAPTURE), SHELL / RESEARCH are refused by the API as kind_invalid. |
| project_id* | string | Working project (UUID). Its Decisions and Agent Questions lists are created on the first cycle (coord_ensure_lists). Requires the org human authority to be configured (coord_set_authority) — otherwise 403. |
| build_brief* | string | The executor prompt (1-40000 chars). Executed verbatim by the local Claude Code runner as a CODE:/SHELL: task — write it as a complete instruction, name the files, forbid nothing the runner already forbids (deploy/commit/push are refused by its context). |
| preview_url | string | Optional https preview link shown on the Decision task. |
| review_brief* | string | The reviewer prompt (1-40000 chars). The reviewer reads the stored bundle (tascan_get_build / tascan_get_build_file), the task text and the filtered trail; it returns approve, revise, reject or needs_input as strict JSON bound to the build_ref. Still required even when `reviews[]` is given (kept as T2's legacy description text). |
| revisable_by | string | review_only cycles only — who fixes a revise verdict. dispatcher (default): the cycle parks on revise as today and you re-dispatch. executor: a revise spawns a revision CAPTURE task (up to max_revisions) that is born blocked on a question task "Revise the documents, then answer to release" addressed to you — fix the files in the repo, answer that question (tascan_post_message kind=answer), and the capture re-runs on the fixed HEAD. Build cycles always revise through the executor regardless of this field. |
| max_questions | integer | Questions a runner may ask per task before the attempt fails (default 3). |
| max_revisions | integer | Revision cap (default 3): at most max_revisions + 1 builds and reviews. |
| artifact_bytes | object | Optional {path: bytes} for artifact_paths — the byte length of each file in your checkout. The API cannot read the repo, so artifact_over_cap (262144-byte reviewable cap per file) is only checked for paths you give a byte count; every other path is reported under unchecked (artifact_over_cap, unchecked_at_api) — never guessed from an earlier stored bundle. Same on dry_run and the real create. |
| artifact_paths* | array | Repo-relative paths the build binds (1-64). Exactly these files are stored as the bundle and hashed into build_ref; files the executor touches OUTSIDE them fail the scope check and park the cycle. No .., not absolute, no drive letter, no backslash. |
| idempotency_key | string | Optional caller key (≤ 200 chars). The same key within 24 h is refused as idempotency_replay (nothing queued) and printed as DUPLICATE with the existing root_id — safe to retry after a lost answer. |
| integrate_title | string | Optional title template for the Integrate task (default "Integrate: <title>"). |
| checkpoint_title | string | Optional title template for the Decision task (default "Decision: <title>"). |
| max_cost_micro_usd | integer | Per-cycle spend cap summed over every attempt, in micro-USD (default 5000000 = USD 5). A claim that could overrun it is refused (budget_exhausted). |
| integrate_description | string | Optional description template for the Integrate task. |
| checkpoint_description | string | Optional description template for the Decision task; the build_ref, preview and findings summary are appended. |
{
"type": "object",
"required": [
"project_id",
"title",
"build_brief",
"review_brief",
"artifact_paths"
],
"properties": {
"kind": {
"enum": [
"build",
"review_only"
],
"type": "string",
"description": "Cycle kind (default build). review_only = no model builds anything: T1 is \"CAPTURE: <title>\", a local-executor task that stores artifact_paths from the repo at HEAD as the bundle (same build_ref, size cap and scope check as a CODE build) and completes in seconds; T2 REVIEW then reviews that bundle. Use it to get an independent review of documents or of code already committed by hand. Stored as coord.kind_of_cycle on the root."
},
"repo": {
"type": "string",
"description": "Which codebase on the executor the build runs in — an alias from the executor's allowlist (tascan-agent/repos.json), e.g. \"tascan\" (default), \"merchskipper\", \"inkskipper\", \"rangerlizzy\", \"cardvault\", \"take1\", \"fitcheck\", \"safegrid\", \"eternity\", \"maniacs\". artifact_paths are relative to that repo. An unknown alias is refused by the executor (repo_unknown) and the cycle parks."
},
"title": {
"type": "string",
"description": "Short human title (1-200 chars). T1 becomes \"CODE: <title>\" (\"CAPTURE: <title>\" when kind=review_only), T2 \"REVIEW: <title>\"."
},
"dry_run": {
"type": "boolean",
"description": "true = PREFLIGHT only: the API validates the whole dispatch and returns every problem at once (code, path, detail) plus the unchecked[] list (what only the executor / the RPC can decide) and warnings[] — nothing is queued, no idempotency key is consumed. Recommended before every real create. Default false."
},
"reviews": {
"type": "array",
"items": {
"type": "object",
"required": [
"lens",
"brief",
"provider"
],
"properties": {
"lens": {
"type": "string",
"description": "Slug, ^[a-z][a-z0-9-]{0,39}$, unique in the array."
},
"brief": {
"type": "string",
"description": "Cycle-specific context for this lens (1-8000 chars)."
},
"model": {
"type": "string",
"description": "Optional, 1-100 chars."
},
"blocking": {
"type": "boolean",
"description": "Default true. At least one entry in the array must be blocking."
},
"provider": {
"enum": [
"openai",
"anthropic",
"gemini"
],
"type": "string"
},
"max_tool_calls": {
"type": "integer",
"description": "Optional, provider openai only."
}
}
},
"description": "Optional multi-lens review panel (design item 14a) instead of today's single OpenAI review_brief lens — 1-8 entries, each: {lens: slug matching ^[a-z][a-z0-9-]{0,39}$ unique per array (e.g. \"code-correctness\", \"security\", \"ui-ux\"), brief: 1-8000 chars of cycle-specific context for that lens, provider: \"openai\"|\"anthropic\"|\"gemini\", model?: 1-100 chars, blocking?: boolean (default true — at least one entry must be blocking or the whole array is refused), max_tool_calls?: integer (openai only)}. One REVIEW: task mints per lens, all blocked on T1; the Decision task mints only once every blocking lens has approved the same build. Omit for today's single OpenAI code review."
},
"task_type": {
"enum": [
"CODE",
"SHELL",
"RESEARCH"
],
"type": "string",
"description": "T1 prefix (default CODE). CAPTURE is never caller-settable: kind=review_only makes T1 \"CAPTURE:\" itself. With kind=review_only the value is sent as given — CODE is accepted (T1 is still CAPTURE), SHELL / RESEARCH are refused by the API as kind_invalid."
},
"project_id": {
"type": "string",
"description": "Working project (UUID). Its Decisions and Agent Questions lists are created on the first cycle (coord_ensure_lists). Requires the org human authority to be configured (coord_set_authority) — otherwise 403."
},
"build_brief": {
"type": "string",
"description": "The executor prompt (1-40000 chars). Executed verbatim by the local Claude Code runner as a CODE:/SHELL: task — write it as a complete instruction, name the files, forbid nothing the runner already forbids (deploy/commit/push are refused by its context)."
},
"preview_url": {
"type": "string",
"description": "Optional https preview link shown on the Decision task."
},
"review_brief": {
"type": "string",
"description": "The reviewer prompt (1-40000 chars). The reviewer reads the stored bundle (tascan_get_build / tascan_get_build_file), the task text and the filtered trail; it returns approve, revise, reject or needs_input as strict JSON bound to the build_ref. Still required even when `reviews[]` is given (kept as T2's legacy description text)."
},
"revisable_by": {
"enum": [
"dispatcher",
"executor"
],
"type": "string",
"description": "review_only cycles only — who fixes a revise verdict. dispatcher (default): the cycle parks on revise as today and you re-dispatch. executor: a revise spawns a revision CAPTURE task (up to max_revisions) that is born blocked on a question task \"Revise the documents, then answer to release\" addressed to you — fix the files in the repo, answer that question (tascan_post_message kind=answer), and the capture re-runs on the fixed HEAD. Build cycles always revise through the executor regardless of this field."
},
"max_questions": {
"type": "integer",
"description": "Questions a runner may ask per task before the attempt fails (default 3)."
},
"max_revisions": {
"type": "integer",
"description": "Revision cap (default 3): at most max_revisions + 1 builds and reviews."
},
"artifact_bytes": {
"type": "object",
"description": "Optional {path: bytes} for artifact_paths — the byte length of each file in your checkout. The API cannot read the repo, so artifact_over_cap (262144-byte reviewable cap per file) is only checked for paths you give a byte count; every other path is reported under unchecked (artifact_over_cap, unchecked_at_api) — never guessed from an earlier stored bundle. Same on dry_run and the real create.",
"additionalProperties": {
"type": "integer"
}
},
"artifact_paths": {
"type": "array",
"items": {
"type": "string"
},
"description": "Repo-relative paths the build binds (1-64). Exactly these files are stored as the bundle and hashed into build_ref; files the executor touches OUTSIDE them fail the scope check and park the cycle. No .., not absolute, no drive letter, no backslash."
},
"idempotency_key": {
"type": "string",
"description": "Optional caller key (≤ 200 chars). The same key within 24 h is refused as idempotency_replay (nothing queued) and printed as DUPLICATE with the existing root_id — safe to retry after a lost answer."
},
"integrate_title": {
"type": "string",
"description": "Optional title template for the Integrate task (default \"Integrate: <title>\")."
},
"checkpoint_title": {
"type": "string",
"description": "Optional title template for the Decision task (default \"Decision: <title>\")."
},
"max_cost_micro_usd": {
"type": "integer",
"description": "Per-cycle spend cap summed over every attempt, in micro-USD (default 5000000 = USD 5). A claim that could overrun it is refused (budget_exhausted)."
},
"integrate_description": {
"type": "string",
"description": "Optional description template for the Integrate task."
},
"checkpoint_description": {
"type": "string",
"description": "Optional description template for the Decision task; the build_ref, preview and findings summary are appended."
}
}
}| canary | integer | How many seats release immediately; the rest stay held on T_F until the first canary settles done. Default min(2, seats.length). |
| min_ok | integer | Minimum seats that must finish done before the synth barrier can close. Default ceil(seats.length / 2). |
| dry_run | boolean | true runs the same validation plus the ceiling/cap pre-check and returns {dry_run:true, plan_sha256, seats, problems:[]} WITHOUT consuming budget or creating a fan-out -- a real preflight, not a real create. |
| breaker_k | integer | Paid (non-infrastructure) seat failures before the breaker pauses the fan-out (2-5, default 2). An expired run never counts. |
| project_id* | string | Working project (UUID). The fan-out root T_F is minted on its Questions list (coord_ensure_lists). |
| seat_review | string | per_seat mints one review per seat (allowed for at most 10 seats); none skips seat review. Default per_seat for <=10 seats, none above that. |
| deadline_min | integer | Minutes before an idle seat is dropped and the barrier re-evaluated, and before a stuck synthesizing fan-out closes deadline (10-1440, default 240). |
| seat_defaults | object | Shared defaults applied when a seat names no brief of its own. |
| idempotency_key | string | Optional replay key (<= 200 chars). The same key within 24h on the same plan returns duplicate:true with the existing fanout_id; on a different plan it is refused idempotency_body_mismatch. |
{
"type": "object",
"required": [
"project_id",
"title",
"seats",
"synth",
"cap"
],
"properties": {
"cap": {
"type": "integer",
"description": "This fan-out's total micro-USD budget, synth reserve included. Refused over_ceiling if it exceeds the org's fan-out spending ceiling."
},
"seats": {
"type": "array",
"items": {
"type": "object",
"required": [
"agent_id"
],
"properties": {
"role": {
"type": "string",
"description": "Optional label for this seat (1-100 chars), e.g. \"topic A\"."
},
"brief": {
"type": "string",
"description": "This seat's own prompt (1-40000 chars). Falls back to seat_defaults.brief when omitted -- one of the two is required."
},
"runner": {
"enum": [
"research",
"local"
],
"type": "string",
"description": "Default \"research\" (the cloud runner claims it). \"local\" names a registered local/subagent agent_id -- only that agent, claiming as itself (tascan_claim_run instance starting with \"<agent_id>:\"), can claim the seat; it is never webhook-fired."
},
"agent_id": {
"type": "string",
"description": "A registered agent_registry id in this org, matching ^[a-z][a-z0-9-]{0,39}$. Never a system-* worker."
}
}
},
"description": "1-30 seats. Each names the agent that runs it; brief/role fall back to seat_defaults when omitted."
},
"synth": {
"type": "object",
"required": [
"brief",
"max_cost_micro_usd"
],
"properties": {
"brief": {
"type": "string",
"description": "The synthesizer's prompt (1-40000 chars)."
},
"max_cost_micro_usd": {
"type": "integer",
"description": "Positive integer micro-USD reserved for the synth run; reserved out of cap up front, released back into the seat budget only at barrier close."
}
},
"description": "The synthesizer that runs once the barrier closes, reading every done seat's stored output as DATA."
},
"title": {
"type": "string",
"description": "Short human title (1-200 chars). T_F becomes \"FANOUT: <title>\"; seats become \"RESEARCH: [seat-NN] <title>\"; the synth becomes \"RESEARCH: [synth] <title>\"."
},
"canary": {
"type": "integer",
"description": "How many seats release immediately; the rest stay held on T_F until the first canary settles done. Default min(2, seats.length)."
},
"min_ok": {
"type": "integer",
"description": "Minimum seats that must finish done before the synth barrier can close. Default ceil(seats.length / 2)."
},
"dry_run": {
"type": "boolean",
"description": "true runs the same validation plus the ceiling/cap pre-check and returns {dry_run:true, plan_sha256, seats, problems:[]} WITHOUT consuming budget or creating a fan-out -- a real preflight, not a real create."
},
"breaker_k": {
"type": "integer",
"description": "Paid (non-infrastructure) seat failures before the breaker pauses the fan-out (2-5, default 2). An expired run never counts."
},
"project_id": {
"type": "string",
"description": "Working project (UUID). The fan-out root T_F is minted on its Questions list (coord_ensure_lists)."
},
"seat_review": {
"enum": [
"per_seat",
"none"
],
"type": "string",
"description": "per_seat mints one review per seat (allowed for at most 10 seats); none skips seat review. Default per_seat for <=10 seats, none above that."
},
"deadline_min": {
"type": "integer",
"description": "Minutes before an idle seat is dropped and the barrier re-evaluated, and before a stuck synthesizing fan-out closes deadline (10-1440, default 240)."
},
"seat_defaults": {
"type": "object",
"required": [
"est_micro_usd"
],
"properties": {
"brief": {
"type": "string",
"description": "Default seat prompt (1-40000 chars), used by any seat that names no brief of its own."
},
"est_micro_usd": {
"type": "integer",
"description": "Positive integer micro-USD estimate per seat, used by the RPC's cap_below_plan check."
},
"review_est_micro_usd": {
"type": "integer",
"description": "Non-negative integer micro-USD estimate per seat review. Must be > 0 when seat_review is per_seat."
}
},
"description": "Shared defaults applied when a seat names no brief of its own."
},
"idempotency_key": {
"type": "string",
"description": "Optional replay key (<= 200 chars). The same key within 24h on the same plan returns duplicate:true with the existing fanout_id; on a different plan it is refused idempotency_body_mismatch."
}
}
}| array |
| Explicit lines instead of auto-billing |
| project_id | string | Bill every list in this project (auto line items) |
| client_name* | string | Bill-to name (person or company) |
| hourly_rate | number | Dollars per hour for auto line items |
| client_email | string |
| client_phone | string |
| company_name | string | Your company name on the attached Service Report (defaults to the org name) |
| task_list_ids | array | Bill just these lists (auto line items) |
| payment_options | object | Pay-how-you-like buttons on the invoice (defaults to the org's saved handles). Keys: venmo (@handle), cashapp ($cashtag), paypal (paypal.me name), zelle (phone/email), applecash (phone), other (free text e.g. "cash or check"). Stripe card checkout is separate and only shows when the org has invoice_payments enabled. |
| flat_rate_per_list | number | Dollars per list for auto line items (used when no hourly_rate) |
| attach_service_report | boolean | Mint + link a Service Report for single-list invoices (default true) |
{
"type": "object",
"required": [
"client_name"
],
"properties": {
"notes": {
"type": "string",
"description": "Payment terms / thank-you shown on the invoice"
},
"status": {
"enum": [
"draft",
"sent"
],
"type": "string",
"description": "Default sent"
},
"billing": {
"type": "object",
"properties": {
"mode": {
"enum": [
"hourly",
"day_rate",
"flat"
],
"type": "string"
},
"day_rate": {
"type": "number"
},
"expenses": {
"type": "array",
"items": {
"type": "object",
"required": [
"description",
"amount"
],
"properties": {
"amount": {
"type": "number"
},
"quantity": {
"type": "number"
},
"description": {
"type": "string"
}
}
}
},
"overtime": {
"type": "boolean"
},
"per_diem": {
"type": "number",
"description": "Dollars per work day"
},
"min_hours": {
"type": "number"
},
"hourly_rate": {
"type": "number"
},
"per_diem_days": {
"type": "number"
},
"flat_rate_per_list": {
"type": "number"
},
"overtime_multiplier": {
"type": "number"
},
"overtime_after_hours": {
"type": "number"
},
"double_time_multiplier": {
"type": "number"
},
"double_time_after_hours": {
"type": "number"
}
},
"description": "Billing rules for auto line items. mode: hourly (default when hourly_rate given) | day_rate | flat. Overtime/double time are computed PER WORK DAY from verified completions: hours over overtime_after_hours (default 8) bill at overtime_multiplier (1.5×), hours over double_time_after_hours (12) at double_time_multiplier (2×); set overtime:false to disable. per_diem adds one line × work days (or per_diem_days). expenses are pass-through lines."
},
"due_date": {
"type": "string",
"description": "YYYY-MM-DD (default: 30 days out)"
},
"tax_rate": {
"type": "number",
"description": "Fraction, e.g. 0.0825 for 8.25%"
},
"min_hours": {
"type": "number",
"description": "Minimum billable hours per list (e.g. 1)"
},
"line_items": {
"type": "array",
"items": {
"type": "object",
"required": [
"description",
"unit_price"
],
"properties": {
"quantity": {
"type": "number"
},
"unit_price": {
"type": "number"
},
"description": {
"type": "string"
}
}
},
"description": "Explicit lines instead of auto-billing"
},
"project_id": {
"type": "string",
"description": "Bill every list in this project (auto line items)"
},
"client_name": {
"type": "string",
"description": "Bill-to name (person or company)"
},
"hourly_rate": {
"type": "number",
"description": "Dollars per hour for auto line items"
},
"client_email": {
"type": "string"
},
"client_phone": {
"type": "string"
},
"company_name": {
"type": "string",
"description": "Your company name on the attached Service Report (defaults to the org name)"
},
"task_list_ids": {
"type": "array",
"items": {
"type": "string"
},
"description": "Bill just these lists (auto line items)"
},
"payment_options": {
"type": "object",
"properties": {
"other": {
"type": "string"
},
"venmo": {
"type": "string"
},
"zelle": {
"type": "string"
},
"paypal": {
"type": "string"
},
"cashapp": {
"type": "string"
},
"applecash": {
"type": "string"
}
},
"description": "Pay-how-you-like buttons on the invoice (defaults to the org's saved handles). Keys: venmo (@handle), cashapp ($cashtag), paypal (paypal.me name), zelle (phone/email), applecash (phone), other (free text e.g. \"cash or check\"). Stripe card checkout is separate and only shows when the org has invoice_payments enabled."
},
"flat_rate_per_list": {
"type": "number",
"description": "Dollars per list for auto line items (used when no hourly_rate)"
},
"attach_service_report": {
"type": "boolean",
"description": "Mint + link a Service Report for single-list invoices (default true)"
}
}
}| description |
| string |
| Shown to workers on the Site Gate page |
| notify_email | string | Alert recipient override — defaults to all org admins |
| required_ppe | array | Hazard zones: PPE the worker must show at entry (pick-list so the audit reads the same words) |
| task_list_id | string | Task list the Site Gate routes workers to when they are inside this zone |
| auto_clock_in | boolean | Writes a shift_start when the WORKER PAGE, while open, sees the phone enter the zone; detection is pull-based, there is no background geofencing. For a background-free presence event use a gate NFC/QR scan. |
| enter_message | string | What the worker sees / is texted on entry (default is generated from the rule) |
| auto_clock_out | boolean | Writes a shift_end when the WORKER PAGE, while open, sees the phone leave the zone; detection is pull-based, there is no background geofencing. For a background-free presence event use a gate NFC/QR scan. |
| notify_on_exit | boolean | Email the manager when a worker leaves this zone (accountability — sign in then disappear) |
| alert_on_breach | boolean | Email + SMS the admin on containment-exit / restricted-enter (default true) |
| enforce_on_list | boolean | Zone-lock the task list — it cannot be started from outside the radius |
| notify_on_enter | boolean | Email the manager when a worker enters this zone (danger areas) |
| ppe_photo_required | boolean | Hazard zones: pop a photo checkpoint on entry (default true when required_ppe is set) |
| sms_worker_on_exit | boolean | Text the worker on exit |
| task_list_on_enter | string | Task list dispatched to the worker (in-app + SMS) when they cross into the zone |
| sms_worker_on_enter | boolean | Text the worker the rule/list on entry, even if the app is closed |
{
"type": "object",
"required": [
"name",
"lat",
"lng"
],
"properties": {
"lat": {
"type": "number",
"description": "Zone center latitude"
},
"lng": {
"type": "number",
"description": "Zone center longitude"
},
"kind": {
"enum": [
"work_site",
"hazard",
"containment",
"restricted"
],
"type": "string",
"description": "What the fence MEANS. work_site: expected here (auto clock-in, list on enter). hazard: enter allowed under conditions — required_ppe + photo checkpoint verified by AI vision, the OSHA row. containment: must stay inside; leaving = breach. restricted: must stay out; entering = breach. Default work_site."
},
"name": {
"type": "string",
"description": "Zone name (e.g. \"Stage Left\", \"Loading Dock\")"
},
"polygon": {
"type": "array",
"items": {
"type": "array",
"items": {
"type": "number"
}
},
"description": "Polygon/rectangle zone instead of a circle: vertices as [[lat,lng], ...], at least 3. lat/lng/radius_m are then computed (centroid + bounding radius) — still pass lat/lng but they are overridden."
},
"radius_m": {
"type": "number",
"description": "Radius in meters (default 150, min 10, max 100000)"
},
"project_id": {
"type": "string",
"description": "Project this zone belongs to"
},
"description": {
"type": "string",
"description": "Shown to workers on the Site Gate page"
},
"notify_email": {
"type": "string",
"description": "Alert recipient override — defaults to all org admins"
},
"required_ppe": {
"type": "array",
"items": {
"enum": [
"hard_hat",
"safety_glasses",
"hi_vis_vest",
"gloves",
"steel_toe_boots",
"hearing_protection",
"harness",
"respirator",
"face_shield"
],
"type": "string"
},
"description": "Hazard zones: PPE the worker must show at entry (pick-list so the audit reads the same words)"
},
"task_list_id": {
"type": "string",
"description": "Task list the Site Gate routes workers to when they are inside this zone"
},
"auto_clock_in": {
"type": "boolean",
"description": "Writes a shift_start when the WORKER PAGE, while open, sees the phone enter the zone; detection is pull-based, there is no background geofencing. For a background-free presence event use a gate NFC/QR scan."
},
"enter_message": {
"type": "string",
"description": "What the worker sees / is texted on entry (default is generated from the rule)"
},
"auto_clock_out": {
"type": "boolean",
"description": "Writes a shift_end when the WORKER PAGE, while open, sees the phone leave the zone; detection is pull-based, there is no background geofencing. For a background-free presence event use a gate NFC/QR scan."
},
"notify_on_exit": {
"type": "boolean",
"description": "Email the manager when a worker leaves this zone (accountability — sign in then disappear)"
},
"alert_on_breach": {
"type": "boolean",
"description": "Email + SMS the admin on containment-exit / restricted-enter (default true)"
},
"enforce_on_list": {
"type": "boolean",
"description": "Zone-lock the task list — it cannot be started from outside the radius"
},
"notify_on_enter": {
"type": "boolean",
"description": "Email the manager when a worker enters this zone (danger areas)"
},
"ppe_photo_required": {
"type": "boolean",
"description": "Hazard zones: pop a photo checkpoint on entry (default true when required_ppe is set)"
},
"sms_worker_on_exit": {
"type": "boolean",
"description": "Text the worker on exit"
},
"task_list_on_enter": {
"type": "string",
"description": "Task list dispatched to the worker (in-app + SMS) when they cross into the zone"
},
"sms_worker_on_enter": {
"type": "boolean",
"description": "Text the worker the rule/list on entry, even if the app is closed"
}
}
}| display_name |
| string |
| Readable id for the agent, e.g. "LT-ZOOS-RSCH-02 · Zoos · Researcher 02". |
| resource_constraints | object | Optional project sandbox: { "project_ids": ["<project uuid>", ...] }. Enforced on every call (migration 249): the child reaches only those projects. Must be a subset of this key’s own projects when this key is itself project-limited; omitted, the child inherits this key’s projects. |
{
"type": "object",
"required": [
"scopes"
],
"properties": {
"role": {
"type": "string",
"description": "The agent’s job on the project: builder, researcher, reviewer, panelist, ..."
},
"model": {
"type": "string",
"description": "The model the agent runs on, e.g. claude-sonnet-5-5."
},
"scopes": {
"type": "array",
"items": {
"enum": [
"read",
"write"
],
"type": "string"
},
"description": "Scopes to request for the child key — read and/or write only."
},
"vendor": {
"enum": [
"anthropic",
"openai",
"gemini"
],
"type": "string",
"description": "The model vendor (defaults to this key’s own vendor)."
},
"agent_id": {
"type": "string",
"description": "Stable agent id used in receipts and the agent registry."
},
"ttl_seconds": {
"type": "number",
"description": "How long the child key lives, in seconds (default 3600 = 1h; clamped to [60, 86400] and to this key's own expiry)."
},
"display_name": {
"type": "string",
"description": "Readable id for the agent, e.g. \"LT-ZOOS-RSCH-02 · Zoos · Researcher 02\"."
},
"resource_constraints": {
"type": "object",
"description": "Optional project sandbox: { \"project_ids\": [\"<project uuid>\", ...] }. Enforced on every call (migration 249): the child reaches only those projects. Must be a subset of this key’s own projects when this key is itself project-limited; omitted, the child inherits this key’s projects."
}
}
}{
"type": "object",
"required": [
"issue_id",
"instruction"
],
"properties": {
"ai_agent": {
"type": "string",
"description": "Name of the AI agent dispatching (default: TaScan AI)"
},
"issue_id": {
"type": "string",
"description": "Issue ID this instruction relates to"
},
"send_sms": {
"type": "boolean",
"description": "Send SMS to worker (default: true if phone on file)"
},
"worker_id": {
"type": "string",
"description": "Target worker ID (defaults to the worker who reported the issue)"
},
"instruction": {
"type": "string",
"description": "Clear, actionable instruction for the worker to execute"
},
"remediation_tasks": {
"type": "array",
"items": {
"type": "object"
},
"description": "Optional array of tasks to create as a remediation task list. Each: { title, description, response_type, requires_photo, is_safety_checkpoint, sort_order }"
},
"recommendation_summary": {
"type": "string",
"description": "One-line summary for the task list description"
}
}
}| verification |
| string |
| Optional second document (a short verification/self-check note) -- stored as the seat's second artifact_path when the fan-out named two. |
| cost_micro_usd | integer | Optional micro-USD cost to record against the cycle's own budget (default 0 -- a Claude Code subscription seat has no per-call API cost to report). |
{
"type": "object",
"required": [
"run_id",
"instance",
"outcome"
],
"properties": {
"error": {
"type": "string",
"description": "Required when outcome is failed -- why (up to 4000 chars)."
},
"run_id": {
"type": "string",
"description": "The run ID from tascan_claim_run's response."
},
"outcome": {
"enum": [
"completed",
"failed"
],
"type": "string",
"description": "'completed' stores your document and mints a signed completion; 'failed' stores nothing."
},
"document": {
"type": "string",
"description": "Required when outcome is completed. The seat's deliverable -- hashed as build_ref, stored verbatim under the seat's own artifact_paths."
},
"instance": {
"type": "string",
"description": "The SAME instance string you claimed with."
},
"verification": {
"type": "string",
"description": "Optional second document (a short verification/self-check note) -- stored as the seat's second artifact_path when the fan-out named two."
},
"cost_micro_usd": {
"type": "integer",
"description": "Optional micro-USD cost to record against the cycle's own budget (default 0 -- a Claude Code subscription seat has no per-call API cost to report)."
}
}
}| Service report: show worker names (default true) |
| send_to_phone | string | Text the link to this number (E.164 or 10-digit US) |
{
"type": "object",
"required": [
"type"
],
"properties": {
"type": {
"enum": [
"completion",
"service",
"project",
"evidence"
],
"type": "string"
},
"list_id": {
"type": "string",
"description": "Required for completion / service"
},
"message": {
"type": "string",
"description": "Service report: a note to the client shown under the header"
},
"send_note": {
"type": "string",
"description": "Short intro for the text, e.g. \"Here is your report from Love Productions:\""
},
"project_id": {
"type": "string",
"description": "Required for project / evidence"
},
"show_issues": {
"type": "boolean",
"description": "Service report: include reported issues (default false)"
},
"company_name": {
"type": "string",
"description": "Service report branding (defaults to the org name)"
},
"show_workers": {
"type": "boolean",
"description": "Service report: show worker names (default true)"
},
"send_to_phone": {
"type": "string",
"description": "Text the link to this number (E.164 or 10-digit US)"
}
}
}| string |
| Optional 64-hex sha256 of the file at base_ref. |
| build_sha256* | string | 64-hex sha256 of the file at build_ref/path — must match the stored artifact's own sha256. |
{
"type": "object",
"required": [
"build_ref",
"path",
"base_ref",
"text",
"build_sha256"
],
"properties": {
"path": {
"type": "string",
"description": "Repo-relative path from the build manifest (tascan_get_build)."
},
"text": {
"type": "string",
"description": "The already-computed diff text (up to 262144 characters). This tool never computes a diff itself."
},
"base_ref": {
"type": "string",
"description": "The commit-ish the diff was computed against (1-200 chars)."
},
"build_ref": {
"type": "string",
"description": "sha256:<64 hex> (or the bare 64 hex) of the build the diff is about."
},
"truncated": {
"type": "boolean",
"description": "True if text is itself a truncated diff."
},
"base_sha256": {
"type": "string",
"description": "Optional 64-hex sha256 of the file at base_ref."
},
"build_sha256": {
"type": "string",
"description": "64-hex sha256 of the file at build_ref/path — must match the stored artifact's own sha256."
}
}
}| string |
| YYYY-MM-DD: only projects whose dates start after this day |
{
"type": "object",
"properties": {
"ref": {
"type": "string",
"description": "Only projects whose external_refs carry this ref (exact, e.g. P14-4680)"
},
"zone": {
"type": "string",
"description": "IANA time zone for the date filters, e.g. America/Los_Angeles (default: the org time zone)"
},
"number": {
"type": "string",
"description": "Only the project with this project number (exact, e.g. P-00012)"
},
"status": {
"type": "string",
"description": "Only projects with this status (exact; comma-separated list allowed, e.g. \"active,archived\")"
},
"window": {
"enum": [
"week",
"month"
],
"type": "string",
"description": "Only projects overlapping this week (Sunday-Saturday) or this calendar month"
},
"ends_before": {
"type": "string",
"description": "YYYY-MM-DD: only projects whose dates end before this day"
},
"starts_after": {
"type": "string",
"description": "YYYY-MM-DD: only projects whose dates start after this day"
}
}
}| idempotency_key* |
| string |
| Required, 1-200 chars. The same key on the same org replays the same stored row. |
{
"type": "object",
"required": [
"actor",
"action",
"object",
"occurred_at",
"idempotency_key"
],
"properties": {
"actor": {
"type": "object",
"required": [
"kind",
"id"
],
"properties": {
"id": {
"type": "string",
"description": "The actor's id."
},
"kind": {
"enum": [
"worker",
"agent",
"device"
],
"type": "string",
"description": "worker requires an active, unmerged worker id in your organization; agent/device ids are recorded as given."
}
},
"description": "Who did it."
},
"action": {
"type": "string",
"description": "What happened, e.g. inspected, scanned, calibrated."
},
"object": {
"type": "object",
"required": [
"kind",
"ref"
],
"properties": {
"ref": {
"type": "string",
"description": "The object reference (asset id, tag id, serial number, or URL)."
},
"kind": {
"enum": [
"asset",
"tag",
"serial",
"url"
],
"type": "string",
"description": "What kind of thing object.ref names."
}
},
"description": "What it happened to."
},
"location": {
"type": "object",
"properties": {
"lat": {
"type": "number"
},
"lng": {
"type": "number"
},
"zone_id": {
"type": "string",
"description": "Optional geofence zone UUID."
}
},
"description": "Optional location."
},
"provenance": {
"type": "object",
"description": "Optional provenance object. provenance.kind first_party_scan is written only by the scan_events trigger and is refused from a caller; provenance.signature triggers device-signature verification."
},
"correlation": {
"type": "object",
"properties": {
"task_id": {
"type": "string",
"description": "A task UUID in your organization to correlate this evidence to."
},
"task_list_id": {
"type": "string",
"description": "A task list UUID in your organization to correlate this evidence to (used when task_id is absent or does not resolve)."
}
},
"description": "Optional correlation hint."
},
"occurred_at": {
"type": "string",
"description": "ISO timestamp for when the event actually happened."
},
"idempotency_key": {
"type": "string",
"description": "Required, 1-200 chars. The same key on the same org replays the same stored row."
}
}
}| addressee |
| string |
| Optional, kind=question only: who the question is for (recorded; nobody is paged). |
| build_ref | string | Optional sha256:<64 hex> the message is about (defaults to the cycle task's bound build). |
| idempotency_key | string | Optional replay key (≤ 200 chars): the same key on the same task returns the same message id. |
{
"type": "object",
"required": [
"task_id",
"kind",
"body"
],
"properties": {
"body": {
"type": "string",
"description": "The message text (1-8000 chars). Treated as DATA by every reader; on a cycle task it may be prepended to the executor prompt as TRAIL INPUT."
},
"kind": {
"enum": [
"question",
"answer",
"handoff",
"discussion"
],
"type": "string",
"description": "question = a question for the record (does not block anything); answer = answers a dispatcher question task and releases the asker; handoff = hand work or context to the next agent; discussion = a note."
},
"task_id": {
"type": "string",
"description": "Task ID (UUID). For an answer: the QUESTION task id (tascan_get_task on the asker shows coord.blocked_by)."
},
"reply_to": {
"type": "string",
"description": "Optional message id this replies to."
},
"addressee": {
"enum": [
"human",
"dispatcher"
],
"type": "string",
"description": "Optional, kind=question only: who the question is for (recorded; nobody is paged)."
},
"build_ref": {
"type": "string",
"description": "Optional sha256:<64 hex> the message is about (defaults to the cycle task's bound build)."
},
"idempotency_key": {
"type": "string",
"description": "Optional replay key (≤ 200 chars): the same key on the same task returns the same message id."
}
}
}| array |
| Task type prefixes this agent handles (e.g. ["RESEARCH", "WRITE"]) |
{
"type": "object",
"required": [
"id",
"name",
"type",
"inbox_id",
"capabilities"
],
"properties": {
"id": {
"type": "string",
"description": "Unique agent ID (e.g. \"my-agent-1\")"
},
"name": {
"type": "string",
"description": "Display name (e.g. \"Research Bot\")"
},
"type": {
"enum": [
"local",
"cloud",
"hybrid"
],
"type": "string",
"description": "Agent type"
},
"model": {
"type": "string",
"description": "Model powering this agent (e.g. \"claude-sonnet-4-6\")"
},
"inbox_id": {
"type": "string",
"description": "Task list ID this agent monitors for new tasks"
},
"location": {
"type": "string",
"description": "Where the agent runs (e.g. \"AWS us-east-1\")"
},
"worker_id": {
"type": "string",
"description": "TaScan worker ID for this agent"
},
"description": {
"type": "string",
"description": "What this agent does"
},
"capabilities": {
"type": "array",
"items": {
"type": "string"
},
"description": "Task type prefixes this agent handles (e.g. [\"RESEARCH\", \"WRITE\"])"
}
}
}{
"type": "object",
"required": [
"task_list_id",
"worker_id",
"amount_cents",
"payer_email"
],
"properties": {
"memo": {
"type": "string",
"description": "What the payment is for (shown to the payer)"
},
"worker_id": {
"type": "string",
"description": "Worker who gets paid"
},
"payer_name": {
"type": "string"
},
"payer_email": {
"type": "string",
"description": "Who pays — receives the pay link on verification"
},
"amount_cents": {
"type": "number",
"description": "Amount in cents ($1 min, $10,000 max)"
},
"task_list_id": {
"type": "string",
"description": "Task list the payment is tied to"
},
"idempotency_key": {
"type": "string",
"description": "B1: optional replay key (up to 1024 chars, no braces) sent verbatim as the Idempotency-Key header instead of a fresh one per call — a retried call with the SAME key replays the original pledge instead of creating a second one."
}
}
}| idempotency_key | string | Optional caller key (≤ 200 chars) — replays return the existing scheduled row |
{
"type": "object",
"required": [
"send_at",
"message"
],
"properties": {
"phone": {
"type": "string",
"description": "Raw phone (E.164) — must already belong to a worker or roster entry of your org"
},
"list_id": {
"type": "string",
"description": "Optional task list ID — appends the tap-to-open checklist link"
},
"message": {
"type": "string",
"description": "Message text (1-400 chars, links stripped). Transactional and work-related only."
},
"send_at": {
"type": "string",
"description": "When to send — ISO 8601 with a timezone offset, e.g. \"2026-09-25T18:30:00-07:00\" (Las Vegas is -07:00 in September). Delivery happens on the next 5-minute tick at or after this time."
},
"worker_id": {
"type": "string",
"description": "Worker ID — texts their phone on file (preferred over raw phone)"
},
"include_link": {
"type": "boolean",
"description": "Append the list link (default true when list_id is given)"
},
"idempotency_key": {
"type": "string",
"description": "Optional caller key (≤ 200 chars) — replays return the existing scheduled row"
}
}
}| When a list_id is given, append the tap-to-open link to the SMS body (default true). Set false to send the message text alone — the link is still returned for you to share another way. |
| media_base64 | string | Optional image to attach (MMS) as base64 bytes (a data: URI prefix is accepted). JPEG, PNG or GIF, max 3.5 MB. Stored privately by TaScan and sent through a short-lived link. |
| idempotency_key | string | B1: optional replay key (up to 1024 chars, no braces) sent verbatim as the Idempotency-Key header instead of a fresh one per call — a retried call with the SAME key replays the original response instead of sending a second text. |
{
"type": "object",
"required": [],
"properties": {
"phone": {
"type": "string",
"description": "Raw phone number (e.g. \"+17025551234\") — used when no worker_id given"
},
"list_id": {
"type": "string",
"description": "Optional task list ID — appends a tap-to-open worker checklist link"
},
"message": {
"type": "string",
"description": "Message text. Transactional and work-related only."
},
"media_url": {
"type": "string",
"description": "Optional image to attach (MMS): a public https URL of a JPEG, PNG or GIF. The carrier fetches it at send time."
},
"worker_id": {
"type": "string",
"description": "Worker ID — sends to their phone on file (preferred over raw phone)"
},
"media_type": {
"enum": [
"image/jpeg",
"image/png",
"image/gif"
],
"type": "string",
"description": "MIME type of media_base64 (not needed when media_base64 is a data: URI)."
},
"include_link": {
"type": "boolean",
"description": "When a list_id is given, append the tap-to-open link to the SMS body (default true). Set false to send the message text alone — the link is still returned for you to share another way."
},
"media_base64": {
"type": "string",
"description": "Optional image to attach (MMS) as base64 bytes (a data: URI prefix is accepted). JPEG, PNG or GIF, max 3.5 MB. Stored privately by TaScan and sent through a short-lived link."
},
"idempotency_key": {
"type": "string",
"description": "B1: optional replay key (up to 1024 chars, no braces) sent verbatim as the Idempotency-Key header instead of a fresh one per call — a retried call with the SAME key replays the original response instead of sending a second text."
}
}
}| array |
| The full list of external references (replaces the current one): up to 10 { source (1-40 chars), ref (1-80 chars) } |
| report_options | object | Merged into the stored options: show_location, show_time, auto_publish_receipts (booleans), client_photos (off | on | ask); a key set to null is removed; null clears them all |
{
"type": "object",
"required": [
"project_id"
],
"properties": {
"name": {
"type": "string",
"description": "New name"
},
"number": {
"type": "string",
"description": "New project number, 1-40 letters, digits, dot, underscore or hyphen, unique in the org. A P-<digits> number is refused 400 unless it is the project's current number"
},
"status": {
"enum": [
"active",
"archived"
],
"type": "string",
"description": "Status"
},
"end_date": {
"type": "string",
"description": "End date (ISO)"
},
"location": {
"type": "string",
"description": "New location"
},
"project_id": {
"type": "string",
"description": "Project ID"
},
"start_date": {
"type": "string",
"description": "Start date (ISO)"
},
"labor_rules": {
"type": "object",
"description": "An object (or null to clear): { ot_after_hours (> 0, <= 24, or null), dt_after_hours (> ot_after_hours, or > 10 when that is null, <= 24, or null) }"
},
"external_refs": {
"type": "array",
"items": {
"type": "object",
"required": [
"source",
"ref"
],
"properties": {
"ref": {
"type": "string"
},
"source": {
"type": "string"
}
}
},
"description": "The full list of external references (replaces the current one): up to 10 { source (1-40 chars), ref (1-80 chars) }"
},
"report_options": {
"type": "object",
"description": "Merged into the stored options: show_location, show_time, auto_publish_receipts (booleans), client_photos (off | on | ask); a key set to null is removed; null clears them all"
}
}
}| Response configuration. For "choice": {options: [...]}. See tascan_add_tasks. |
| required_equipment | string | Equipment this task depends on (free text) |
| is_safety_checkpoint | boolean | Safety-critical flag |
{
"type": "object",
"required": [
"task_id"
],
"properties": {
"title": {
"type": "string",
"description": "New title"
},
"task_id": {
"type": "string",
"description": "Task ID"
},
"sort_order": {
"type": "number",
"description": "Sort position"
},
"assigned_to": {
"type": "string",
"description": "Worker to assign this task to — pass the worker UUID (validated against your org and stored as the worker's name, which is how the worker portal matches \"my tasks\"). A plain name string is also accepted as-is. Pass null to clear."
},
"description": {
"type": "string",
"description": "New description"
},
"response_type": {
"enum": [
"checkbox",
"photo",
"text",
"number",
"date",
"choice",
"link",
"url",
"file",
"signature"
],
"type": "string",
"description": "See tascan_add_tasks for guidance. \"text\" for info collection, \"photo\" for visual proof, \"checkbox\" for yes/no only, \"link\" for the worker submitting a URL, \"url\" for the worker visiting a preset URL and confirming, \"file\" for the worker uploading a file."
},
"requires_photo": {
"type": "boolean",
"description": "Require photo"
},
"response_config": {
"type": "object",
"description": "Response configuration. For \"choice\": {options: [...]}. See tascan_add_tasks."
},
"required_equipment": {
"type": "string",
"description": "Equipment this task depends on (free text)"
},
"is_safety_checkpoint": {
"type": "boolean",
"description": "Safety-critical flag"
}
}
}| auto_clock_in |
| boolean |
| Writes a shift_start when the WORKER PAGE, while open, sees the phone enter the zone; detection is pull-based, there is no background geofencing. For a background-free presence event use a gate NFC/QR scan. |
| enter_message | string |
| auto_clock_out | boolean | Writes a shift_end when the WORKER PAGE, while open, sees the phone leave the zone; detection is pull-based, there is no background geofencing. For a background-free presence event use a gate NFC/QR scan. |
| notify_on_exit | boolean |
| alert_on_breach | boolean |
| enforce_on_list | boolean |
| notify_on_enter | boolean |
| ppe_photo_required | boolean |
| sms_worker_on_exit | boolean |
| task_list_on_enter | string |
| sms_worker_on_enter | boolean |
{
"type": "object",
"required": [
"zone_id"
],
"properties": {
"lat": {
"type": "number"
},
"lng": {
"type": "number"
},
"kind": {
"enum": [
"work_site",
"hazard",
"containment",
"restricted"
],
"type": "string"
},
"name": {
"type": "string"
},
"polygon": {
"type": "array",
"items": {
"type": "array",
"items": {
"type": "number"
}
},
"description": "Replace geometry with a polygon ([[lat,lng],...], ≥3 vertices); pass null to revert to a circle"
},
"zone_id": {
"type": "string",
"description": "Zone ID"
},
"radius_m": {
"type": "number"
},
"is_active": {
"type": "boolean"
},
"notify_email": {
"type": "string"
},
"required_ppe": {
"type": "array",
"items": {
"enum": [
"hard_hat",
"safety_glasses",
"hi_vis_vest",
"gloves",
"steel_toe_boots",
"hearing_protection",
"harness",
"respirator",
"face_shield"
],
"type": "string"
}
},
"task_list_id": {
"type": "string"
},
"auto_clock_in": {
"type": "boolean",
"description": "Writes a shift_start when the WORKER PAGE, while open, sees the phone enter the zone; detection is pull-based, there is no background geofencing. For a background-free presence event use a gate NFC/QR scan."
},
"enter_message": {
"type": "string"
},
"auto_clock_out": {
"type": "boolean",
"description": "Writes a shift_end when the WORKER PAGE, while open, sees the phone leave the zone; detection is pull-based, there is no background geofencing. For a background-free presence event use a gate NFC/QR scan."
},
"notify_on_exit": {
"type": "boolean"
},
"alert_on_breach": {
"type": "boolean"
},
"enforce_on_list": {
"type": "boolean"
},
"notify_on_enter": {
"type": "boolean"
},
"ppe_photo_required": {
"type": "boolean"
},
"sms_worker_on_exit": {
"type": "boolean"
},
"task_list_on_enter": {
"type": "string"
},
"sms_worker_on_enter": {
"type": "boolean"
}
}
}