Deze referentie is alleen in het Engels. Naar het Nederlandse REST API-overzicht

Diese Referenz gibt es nur auf Englisch. Zur deutschen REST-API-Übersicht

Cette référence existe uniquement en anglais. Aller à la présentation de l'API REST en français

Esta referencia solo está en inglés. Ir a la introducción de la API REST en español

Keeping a copy in sync

Keeping a copy in sync

plnnrly has no webhooks. Poll each collection from a high-water mark on updated_at, re-check cheaply, and catch deleted records.

View as Markdown

plnnrly does not push changes to you: there are no webhooks. To keep your own copy of the data up to date, you poll. This page shows a polling loop that does not miss changes and stays well inside your rate budget.

In the examples, <your-url> is the address you sign in to plnnrly on, such as acme.plnnrly.com, or your organisation's own domain.

The high-water mark

Every collection accepts the filter filter[updated_at][gte] and the sort key updated_at. That is all a sync needs:

  1. Read the collection sorted by updated_at, starting 60 seconds before your stored mark.
  2. Follow links.next until it is null.
  3. Insert or update each row in your copy by its id.
  4. Store the highest updated_at you processed as your new mark.
Terminal
curl -G https://<your-url>/api/v2/projects \
  -H "Authorization: Bearer <token>" \
  -H "Accept: application/json" \
  --data-urlencode "filter[updated_at][gte]=2026-10-15T09:00:00Z" \
  --data-urlencode "sort=updated_at,id" \
  --data-urlencode "per_page=100"

links.next in each response is the full URL of the next page, with your filter and sort carried over. Request it as it is:

Terminal
curl "<links.next from the previous response>" \
  -H "Authorization: Bearer <token>"

For the very first run, leave the filter out and read the whole collection the same way.

Things to know:

  • Overlap is intended. Start each run a little before your stored mark, for example 60 seconds before it. A change is stamped when it is made, not when it is saved for good. It can therefore appear after a read that has already passed its timestamp, and the overlap picks it up. Updating your copy by id makes the repeats harmless.
  • Use the exact format. A timestamp in a filter is UTC with a Z and no fractions of a second, such as 2026-10-15T09:00:00Z. See Data formats.
  • A row can appear twice in one run. When a row changes while you are paging, it moves to the end of the sort order and comes back later in the same run.
  • Related lists are collections of their own. updated_at belongs to the row itself. Poll lists that belong to a record, such as a work item's comments, separately.
  • Some collections default to your own rows. /worklogs, for example, returns only your own entries unless you name other users in filter[user_id], which needs an extra permission. The operation page says so. See Filtering and sorting.

Cheap re-checks

Small lists that you re-read in full, such as labels or workflow statuses, have a cheaper check. Send the page's weak ETag back as If-None-Match:

Terminal
curl -i https://<your-url>/api/v2/labels \
  -H "Authorization: Bearer <token>" \
  -H 'If-None-Match: W/"a1b2c3d4e5f6"'

An unchanged page answers 304 Not Modified with no body, and costs 1 unit. A single record works the same way with its own ETag. See Concurrency and ETags.

Deletions

Records disappear in two ways. Deleting and restoring lists which resources do what.

Archived records. On collections whose rows carry deleted_at, a delete archives the record. Add trashed=with to your sync query. Archiving and restoring both move updated_at, so the change arrives through your high-water mark, with deleted_at set or cleared.

Permanent deletes leave no trace to poll for. That includes a project deleted with force=true. Find them with a periodic id scan: read the whole collection by id, and remove from your copy every row the scan did not return.

Terminal
curl -G https://<your-url>/api/v2/projects \
  -H "Authorization: Bearer <token>" \
  --data-urlencode "sort=id" \
  --data-urlencode "trashed=with" \
  --data-urlencode "per_page=100" \
  --data-urlencode "view=summary"

Keep trashed=with in the scan on collections that archive, or archived rows look deleted. A row you are no longer allowed to see also drops out of the scan.

What it costs

Take a sync of projects, customers and work items every 5 minutes, plus a nightly id scan. Costs follow Rate limits:

StepRequestsUnits
A run where nothing changed: one first page per collection33
A run that finds 250 changed work items: three pages of up to 100 rows, plus one page each for projects and customers55
The same run with include=labels on the work items: a to-many path limits the page to 25 rows and adds 3 units per page1242
A nightly id scan of 12,000 work items, at 100 per page120120

Every step stays far below the 600 read units a token may spend per minute. Keep includes out of the sync loop unless you need them: they cost more than they look.

A separate read-only token for the sync gives it its own per-token budget. The organisation's budget is still shared with every other token. See API tokens.

Do not sync over MCP

The MCP server is made for an AI assistant answering questions. It allows 60 requests per minute per user, lists default to 10 rows, and an answer is cut off at 30,000 characters: see Tools. Sync with the REST API and a token instead.