Black-and-white line illustration of a warning triangle and tools for debugging and fixes.
Black-and-white line illustration of a warning triangle and tools for debugging and fixes.

Checking for changes

Polling for Response Changes

The response feeds endpoint allows developers to track changes to participant responses incrementally, rather than repeatedly fetching entire datasets. Each feed query returns a list of response events (created, updated, rejected, or deleted) along with a cursor that marks the current position in the change log.

Developers can:

  • Request a feed for a specific testId or across multiple tests.

  • Use the returned cursor token to fetch only new events since the last poll.

  • Handle batched changes efficiently with predictable ordering.

  • Reduce load on clients and servers by avoiding full refreshes.

This makes it simple to keep a local data store, dashboard, or integration layer in sync with the latest state of responses.

Optimizing change detection with testResultChangesCount

For applications that poll frequently or need to minimize API calls, the testResultChangesCount query provides a more performant way to check for changes before fetching detailed data.

Why use testResultChangesCount?

Instead of fetching all change events every time you poll, testResultChangesCount returns:

  • count — The number of unique test results that have changed

  • latestCursor — The cursor of the most recent change

This allows you to:

  1. Check if any changes exist without transferring full change data

  2. Get the latest cursor to use in subsequent testResultChanges queries

  3. Skip unnecessary API calls when no changes have occurred

  4. Reduce bandwidth by only fetching detailed changes when needed

Query structure

query checkForChanges {
  testResultChangesCount(testUUID: "ut_74b72d9f-fe10-4ae0-83ee-acc61b431e1b") {
    count
    latestCursor
  }
}

With sinceCursor parameter

query checkForNewChanges {
  testResultChangesCount(
    testUUID: "ut_74b72d9f-fe10-4ae0-83ee-acc61b431e1b"
    sinceCursor: "ut_16898ccbfd404ee792df4a4a5ae884b0"
  ) {
    count
    latestCursor
  }
}

Check for changes since a specific cursor:

{
  "data": {
    "testResultChangesCount": {
      "count": 3,
      "latestCursor": "ut_801c0cb2d10043b4bc37a3c29df54a1d"
    }
  }
}

If no changes have occurred:

{
  "data": {
    "testResultChangesCount": {
      "count": 0,
      "latestCursor": "ut_16898ccbfd404ee792df4a4a5ae884b0"
    }
  }
}

Finding new or updated responses

We offer a feed of response updates for a specific test that you can to keep track of responses that have been created, updated, rejected or deleted since your last check.

How it works

When you request the testResultChanges query for the first time you will receive the full change history for a specific test.

Each change entry returns a cursor - you should store this.

When you make subsequent requests you can pass the stored cursor as the sinceCursor argument to receive back only changes that have occurred since your previous check.

Example query

Here we're going to select a UserTestNode using its UUID. For an explanation of how to find a test's UUID, see Projects.

First request

query changeFeed {
  testResultChanges(testUUID: "ut_74b72d9f-fe10-4ae0-83ee-acc61b431e1b") {
    edges {
      node {
        cursor
        responseUuid
        testResultId
        changeType
        changedAt
      }
    }
  }
}

Subsequent requests

query changeFeed {
  testResultChanges(testUUID: "ut_74b72d9f-fe10-4ae0-83ee-acc61b431e1b", sinceCursor: "ut_16898ccbfd404ee792df4a4a5ae884b0") {
    edges {
      node {
        cursor
        responseUuid
        testResultId
        changeType
        changedAt
      }
    }
  }
}

Example response

Please note that responses are not available in the feed until they're finalised, at which point they'll be recorded as UPDATED.

{
  "extensions": {
    "ratelimit": {
      "window_reset": "2025-10-01T13:59:00+00:00",
      "cost": 0,
      "remaining": 30000
    }
  },
  "data": {
    "testResultChanges": {
      "edges": [
        {
          "node": {
            "cursor": "ut_801c0cb2d10043b4bc37a3c29df54a1d",
            "responseUuid": "ut_response_25e1df40-f6cc-436e-abe3-ac7d1b6dc533",
            "testResultId": 833433,
            "changeType": "VIDEO_TRANSCRIPTION_UPDATED",
            "changedAt": "2025-10-01T10:37:17.365122+00:00"
          }
        },
        {
          "node": {
            "cursor": "ut_d370ab06745540718bb1e933ead6bdb9",
            "responseUuid": "ut_response_25e1df40-f6cc-436e-abe3-ac7d1b6dc533",
            "testResultId": 833433,
            "changeType": "VIDEO_UPDATED",
            "changedAt": "2025-10-01T10:35:50.472564+00:00"
          }
        },
        {
          "node": {
            "cursor": "ut_a9e9c320448c49958bd51ca8ebbe0e88",
            "responseUuid": "ut_response_25e1df40-f6cc-436e-abe3-ac7d1b6dc533",
            "testResultId": 833433,
            "changeType": "UPDATED",
            "changedAt": "2025-10-01T10:35:46.592420+00:00"
          }
        }
      ]
    }
  }
}

Change types

The changeType field indicates what kind of change occurred:

  • UPDATED — The response data was updated (this is the initial completion event)

  • VIDEO_UPDATED — The video recording was processed or updated

  • VIDEO_TRANSCRIPTION_UPDATED — The transcript for the video was generated or updated

  • REJECTED — The response was rejected (e.g., failed quality checks)