Help center

Filtering and updated_since

Every filter on GET /sops and GET /assignments, what counts as a change to an SOP, and the exact polling pattern that never misses one.

Business plan Updated
On this page
  1. Filters at a glance
  2. updated_since
  3. What counts as a change
  4. The sync pattern
  5. Why include_archived=true
  6. What sync can’t see
  7. Assignment filters

Filters at a glance

Endpoint Parameter Type Effect
GET /sops library_id UUID Only SOPs in this library.
GET /sops board_id UUID Only SOPs in libraries on this board.
GET /sops updated_since ISO 8601 date or date-time Only SOPs whose updated_at is at or after this time.
GET /sops include_archived true or false Add the workspace’s own archived SOPs. Default false.
GET /assignments status open, overdue or completed Only assignments in this state. open includes overdue ones.
GET /assignments assignee_email email Only this member’s assignments. Not case sensitive.
GET /assignments sop_id UUID Only this SOP’s assignments.
GET /search q string The search query. See Search.

Filters combine with AND. An id that matches nothing (a library in another workspace, say) returns an empty list, not an error. A value in the wrong format returns 422 invalid_parameter.

updated_since

updated_since accepts:

  • a date, 2026-09-01, meaning midnight UTC at the start of that day;
  • a date-time with Z, 2026-09-01T14:30:00Z or 2026-09-01T14:30:00.123456Z;
  • a date-time with an offset, 2026-09-01T10:30:00-04:00.

Always send Z or an offset. A date-time without either is interpreted as UTC. In a query string, an offset’s + must be encoded as %2B, or it arrives as a space and the value is refused; curl -G --data-urlencode, URLSearchParams and requests do this for you.

The comparison is inclusive (updated_at >= updated_since), so an SOP that changed exactly at your checkpoint comes back again. That is deliberate: de-duplicating by id is cheap, missing a change is not.

What counts as a change

An SOP’s updated_at moves when:

  • its own fields change: title, description, tags, owner, published flag or review date;
  • it is archived or restored;
  • it is moved to another library;
  • any of its cards or steps are added, edited, reordered or removed.

Things that happen around an SOP, such as a new assignment, a comment or someone viewing it, don’t change updated_at.

The sync pattern

GET /sops is ordered by updated_at ascending (then id), so a sync is a simple loop:

  1. Load your checkpoint: the largest updated_at you have stored. On the first run there is none.
  2. Call GET /sops?updated_since=<checkpoint>&include_archived=true&limit=100 and page through with cursor until has_more is false.
  3. For each SOP, compare its updated_at with your copy. If it’s newer, fetch GET /sops/{id} and store it.
  4. After the last page, save the largest updated_at you saw as the new checkpoint.
Python
import os
import requests

BASE = "https://app.sopcompass.com/api/v1"
session = requests.Session()
session.headers["Authorization"] = f"Bearer {os.environ['SOPCOMPASS_API_KEY']}"


def changed_sops(checkpoint):
    params = {"include_archived": "true", "limit": 100}
    if checkpoint:
        params["updated_since"] = checkpoint
    while True:
        page = session.get(f"{BASE}/sops", params=params, timeout=30).json()
        yield from page["data"]
        if not page["has_more"]:
            return
        params["cursor"] = page["next_cursor"]

Use the SOP’s own updated_at string as the checkpoint, never your server’s clock: the two can disagree, and the API’s value is what the next comparison uses. ISO 8601 strings in this format sort correctly as text.

Why include_archived=true

Archiving an SOP changes its updated_at, but without include_archived=true an archived SOP drops out of the list, so your sync would never see it go. With it, the SOP comes back with archived_at set and you can remove or hide your copy. Restoring it sets archived_at back to null.

What sync can’t see

  • Deleted SOPs and deleted libraries disappear from the API without a trace. To catch them, run an occasional full pass (no updated_since) and remove anything you have that the pass didn’t return.
  • Libraries that stop being shared into the workspace take their SOPs with them. A full pass catches those too.
  • Archived SOPs in shared-in libraries aren’t returned even with include_archived=true; the app’s Archived page shows only the workspace’s own. They drop out of the list, and a full pass catches them.

See Keep a knowledge base in sync for a complete, runnable version.

Assignment filters

Shell
curl -G https://app.sopcompass.com/api/v1/assignments \
  --data-urlencode "status=overdue" \
  --data-urlencode "assignee_email=dana.whitfield@example.com" \
  -H "Authorization: Bearer $SOPCOMPASS_API_KEY"
  • overdue means due_on is before today’s date in UTC and the assignment isn’t completed.
  • open means not completed, so it includes overdue ones. For “open but not overdue”, filter open and drop items whose status is overdue.
  • assignee_email matches the member’s sign-in email. An email that isn’t a member returns an empty list.
  • GET /assignments has no updated_since. To track completions, page from newest and stop once you reach assignments older than anything that could still be open for you, or filter by sop_id or assignee_email.