Skip to main content
Documentation

Tasks

tasks.update

Change exactly one thing about a task — owner, due date or status
WritesWrite budget

REST

Shipping
POST /api/v1/tasks/update

MCP tool

Live
tasks.update

Exposed on: REST API · Shop MCP server · Claude connector · Dash (in-app copilot) · Zapier. Part of the Tasks domain.

Operating contract

ONE CHANGE PER CALL, selected by which parameter you provide: title, notes, priority, complete, reopen, cancel, staff_id (hand it over), unassign, due_at, remove_due_date, link_conversation or unlink_conversation. To make several changes, call this once per change. Supplying none is refused, and so is supplying two — a call that both completed a task and reassigned it would be one audit row covering two decisions, and nobody could tell afterwards which one was intended.

COMPLETE, REOPEN AND CANCEL ARE THREE DIFFERENT OUTCOMES. complete means it was done. cancel means it will not be, and the row stays so 'we were going to and decided not to' is still visible weeks later. reopen puts it back to open and CLEARS the completion stamp, so a task completed by mistake and finished properly later reports the real finish time rather than the first one's.

NOTHING HERE DELETES A TASK, and no capability anywhere in this product does. A task is a record of something a shop said it would do; cancel is the honest version of removing it.

remove_due_date and due_at are separate parameters on purpose rather than one nullable field — clearing a deadline is a decision and it should look like one in the call.

REASSIGNING PINGS THE NEW OWNER. staff_id must be somebody on tasks.assignees; anyone else is refused. Handing a task to whoever already holds it is a no-op with no second notification.

link_conversation re-files the task under a different thread and unlink_conversation detaches it entirely. Use the first when a to-do was created against the wrong thread, and the second only when it genuinely belongs to no conversation — a task with no anchor is the kind nobody can act on.

Who may call it

Permission
inbox.accessThe caller must hold Inbox at the ACT level. A read-only dashboard grant on the same section is refused.
Plan
Every planNo plan gate. Available on every Service VIN plan.
Retries
naturalNaturally idempotent — running it twice leaves the same world as running it once. A retrying integration needs no key.
Rate class
writeCounted against the write budget, which is tighter than a read.

Input

FieldTypeDescription
task_idrequiredstringuuid

The task to change, as returned by tasks.list.

titlestringmax 200 chars, min 1 chars

Rewrite the one-line title. One change per call.

notesstringmax 4000 chars

Replace the notes. Send an empty string to clear them. One change per call.

prioritystring

Set how urgent it is. One change per call.

One of: low, normal, high
completeboolean

True to mark the task done. One change per call.

reopenboolean

True to put a done or cancelled task back to open. One change per call.

cancelboolean

True to cancel it — it will not be done, and the row stays. One change per call.

staff_idstringuuid

Hand it to this teammate, as returned by tasks.assignees. One change per call.

unassignboolean

True to take it off whoever holds it. One change per call.

due_atstringmax 40 chars

ISO instant it is due, resolved against the shop's timezone. One change per call.

remove_due_dateboolean

True to clear the due date entirely. One change per call.

link_conversationstringuuid

Re-file it under this thread, as returned by comms.list_conversations. One change per call.

unlink_conversationboolean

True to detach it from its thread. One change per call.

Output

FieldTypeDescription
idstring

titlestring

notesstring | null

statusstring

prioritystring

due_atstring | null

overdueboolean

completed_atstring | null

created_atstring

conversationstring | null

conversation_labelstring | null

customer_idstring | null

job_idstring | null

message_idstring | null

assigned_tostring | null

assignee_namestring | null

Examples

Built from this capability's own schema — required fields and the ones carrying a default, and nothing invented. Paste one and it validates.

curl
export SERVICEVIN_API_KEY=svk_live_…

curl -X POST https://www.servicevin.com/api/v1/tasks/update \
  -H "Authorization: Bearer $SERVICEVIN_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"task_id":"9b2f1c6e-4a77-4d2b-9f31-0f1c9a8e5d20"}'

TypeScript (fetch)
const res = await fetch("https://www.servicevin.com/api/v1/tasks/update", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.SERVICEVIN_API_KEY}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    "task_id": "9b2f1c6e-4a77-4d2b-9f31-0f1c9a8e5d20"
  }),
});

// Success and failure are both envelopes. Switch on error.code, never
// on error.message — the codes are stable, the messages are for people.
const payload = await res.json();
if (!res.ok) throw new Error(payload.error.code);
const data = payload.data;

Python (requests)
import os, requests

res = requests.post(
    "https://www.servicevin.com/api/v1/tasks/update",
    headers={"Authorization": f"Bearer {os.environ['SERVICEVIN_API_KEY']}"},
    json={
    "task_id": "9b2f1c6e-4a77-4d2b-9f31-0f1c9a8e5d20"
},
    timeout=30,
)
payload = res.json()
if not res.ok:
    raise RuntimeError(payload["error"]["code"])
data = payload["data"]

MCP tools/call — https://www.servicevin.com/api/mcp
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "tasks.update",
    "arguments": {
      "task_id": "9b2f1c6e-4a77-4d2b-9f31-0f1c9a8e5d20"
    }
  }
}

Refusals

The four gates run in this order on every surface, and the order is not arbitrary — see Authentication.

StatusCodeWhen
404not_foundThe id is unknown, or the feature is not enabled for this account. Deliberately the same answer for both.
403forbiddenThis login does not hold inbox.access.
403insufficient_scopeThe credential is read-only and this capability writes.
422validation_errorAn argument was wrong. The message names the field.
429rate_limitedToo many write calls. Back off and retry.
500internal_errorSomething failed on our side. Nothing was changed.