Skip to content
TasksMate Developers

0.x — pre-release, no compatibility promise yet.What this means

What this is: tasksmate, a typed client generated from the same contract as this site. When you need it: any Python that talks to TasksMate — scripts, services, notebooks.

from tasksmate import TasksMate
tm = TasksMate(token="tm_live_…") # or set TASKSMATE_TOKEN and call TasksMate()
for task in tm.tasks.list(org_id="O0020"): # every task, page after page
print(task.task_id, task.status, task.title)
tm.views.rows("V123456").to_dataframe() # a saved view as a pandas DataFrame
  • Token: mint it in TasksMate → Developers → Tokens (it is shown once).
  • OAuth apps: the authorization flow (/oauth/authorize, /oauth/token, /.well-known/*) is not wrapped here — run it with any OAuth 2.1 library; the access token it returns is a token like any other: TasksMate(token=…).
  • Reach: a token belongs to one organization and carries scopes (tasks:read, tasks:write, projects:read, …); each method's docstring names the scope it needs.
  • Try it: examples/quickstart.py runs a full round trip — create, conditional update, filtered list, DataFrame, delete — and leaves nothing behind.
Terminal window
pip install tasksmate # the client (Python 3.10+)
pip install "tasksmate[pandas]" # + DataFrames
pip install "tasksmate[cli]" # + the `tm` command line

Not on PyPI yet: until the first release, install from a checkout — pip install -e ".[pandas,cli]" — or from a built wheel (python -m build, then pip install "dist/tasksmate-<version>-py3-none-any.whl[pandas]").

Every list returns a Page: .data (this page), .next_cursor, and iteration over every item, following the cursor until it is None (a page may be short, or even empty, while more follow — iteration handles it).

page = tm.tasks.list(org_id="O0020", filter={"status": ["in_progress", "blocked"], "search": "invoice"},
sort_by="due_date", limit=200)
page.data, page.next_cursor # the first page
for task in page: ... # all of them
for p in page.pages(): ... # page by page
page.envelope # the full response model (some lists carry sums beside `data`)

filter={key: value} becomes filter[key]=value (a list is comma-joined); an unknown key raises ValueError naming the allowed ones. The grammar — keys, sorts, limits — is the API's list grammar.

Every API error is application/problem+json, and every problem type is its own exception, all subclasses of tasksmate.TasksMateError (itself an APIError) with .status, .type, .title, .detail, .request_id, .errors:

from tasksmate import InsufficientScopeError, NotFoundError, PreconditionFailedError, TasksMateError
try:
tm.tasks.update("T123456", {"status": "completed"})
except InsufficientScopeError as exc:
print("this token needs", exc.required_scope) # e.g. tasks:write
except NotFoundError:
...
except TasksMateError as exc:
print(exc.status, exc.detail, "— quote request_id", exc.request_id)
Status Exceptions
400 BadRequestError · InvalidParameterError · IdempotencyKeyInvalidError
401 AuthenticationError · TokenInvalidError · TokenExpiredError · TokenRevokedError
403 ForbiddenError · InsufficientScopeError · TestTokenReadOnlyError · TokenPolicyError (also 422)
404 · 409 · 412 NotFoundError · ConflictError (IdempotencyKeyInFlightError) · PreconditionFailedError
422 UnprocessableEntityError · ValidationError · IdempotencyKeyReusedError · UrlRefusedError
429 · 5xx RateLimitedError (.retry_after) · InternalError
— APIConnectionError / APITimeoutError (no HTTP answer) · ResponseValidationError (a 2xx this SDK version cannot read — upgrade)
tm.tasks.list(org_id="O0020").to_dataframe() # every page; to_dataframe(all_pages=False) for one
tm.views.rows("V123456").to_dataframe()

One row per item, with the API's own field names:

  • a nested object becomes dotted columns (type_data.severity);
  • a list of scalars becomes one comma-separated string (tags → "api, backend");
  • *_at columns are timezone-aware datetimes, *_date columns datetime.date (missing → None).

Needs the pandas extra — without it you get an ImportError saying so.

from tasksmate import webhooks
def receive(headers, raw_body: bytes):
if not webhooks.verify(SECRET, headers, raw_body): # the RAW body, not re-serialized JSON
return 400
event = webhooks.parse(raw_body) # WebhookEvent: .id .type_ .org_id .data.resource_id …
... # dedupe on event.id — delivery is at-least-once
return 200

verify implements the Standard Webhooks recipe TasksMate signs with:

  • Signature: v1, HMAC-SHA256 over id.timestamp.body, keyed by the base64-decoded part of the whsec_… secret.
  • Replay window: timestamps more than tolerance seconds (300) away are refused.
  • Rotation, both ways: during the 24 h after a secret rotation TasksMate sends two signatures, and you may pass both secrets — verify([new, old], …) — while you switch over.
  • Errors: raise_on_failure=True raises WebhookVerificationError with the reason instead of returning False.

Every method, generated per resource: the API reference shows each operation’s Python call.