Skip to main content

List events

GEThttps://api.fieldtask.example/public/v1/events

Returns changes to customers, customer locations, jobs and members, in order, starting after a cursor you saved from an earlier response.

Use it to keep a copy of FieldTask records up to date by polling, instead of or as well as webhooks. Each event tells you which record changed and the names of the fields that changed. It doesn't include field values: read the current record with the matching list or get endpoint.

The feed covers only these four record families. Quotes, invoices, products and member invitations have no events.

Preview

This page is generated from contract 2026-11-03-review.2, a review export that hasn't been released. FieldTask enables the event feed separately from the rest of the API.

Before you start​

You need:

  1. API access for your organization. An owner or admin requests it in Settings → API connections.
  2. A REST credential: an API token created in Settings → API connections, or an access token from an OAuth client-credentials connection. Tokens from an MCP sign-in don't work with the REST API.
  3. Change sync enabled for your organization. FieldTask enables the feed, then enrolls your organization for each record family separately. Until then the feed returns 404 resource_not_found (not enabled in this environment), 503 sync_unavailable (organization not enrolled) or 403 insufficient_scope (a family you asked for isn't enrolled). Ask FieldTask which families are enabled for your organization.
  4. Read permission for each family you want. See the table below.

For a step-by-step polling design, see the Event feed guide.

Permissions​

This operation has no fixed permission requirement; see the conditions below.

Each record family in resources needs its own read permission:

WhenAdditional permissionIncluding its dependencies
resources includes customer_locationscustomer_locations:read, customers:readcustomer_locations:read, customers:read
resources includes customerscustomers:readcustomers:read
resources includes jobsjobs:readjobs:read
resources includes membersmembers:readmembers:read

If you leave out resources, you get every family that FieldTask has enabled, your organization is enrolled for and the connection can read. If there are none, the request fails with 403 insufficient_scope.

Changing a connection's permissions, even adding an unrelated one, invalidates every cursor issued to it. See Recovery.

Request​

Responses​

OK

Response Headers
    Cache-Control

    Responses must not be cached.

    X-Request-ID

    Server-generated request correlation ID.

Parameters​

ParameterRequiredWhat to send
afterYesnow for your first request, then the page.next_cursor from your previous response. Up to 4,096 characters. URL-encode it.
resourcesNoA comma-separated list of distinct families: customers, customer_locations, jobs, members. Order doesn't matter. Omit it to get every family available to the connection.
limitNoThe maximum number of events per page, from 1 to 100. Defaults to 25.

Send the same resources with every cursor. A cursor only works with the selection it was issued for; any other selection returns 400 invalid_cursor. You can change limit between requests.

We recommend always sending resources. A cursor issued without it is tied to the families that were available at that time, so if FieldTask later enrolls another family for your organization, the cursor stops working and you have to start again.

How to poll​

  1. Bootstrap. Request after=now. The response has no events, and its next_cursor marks the current end of the feed. Save it.
  2. Backfill. Read every record you need with the list endpoints, such as GET /public/v1/customers and GET /public/v1/jobs, and store each record's revision.
  3. Poll. Request after= your saved cursor. Apply the events, then save the new next_cursor. Wait page.retry_after_seconds and repeat.

Take the cursor before you backfill. Changes made while you backfill then also appear in the feed, so nothing is missed. Some of those events describe changes your backfill already contains; revision lets you skip them.

The Event feed guide covers this loop, storage and recovery in more detail.

The page object​

FieldMeaning
next_cursorThe cursor to send as after next time. Every successful response has one, including after=now and pages with no events. Store the latest.
has_moretrue if more events are already waiting after this cursor.
retry_after_secondsHow long to wait before your next request. It's short while has_more is true and longer once you've caught up. Use the value returned; don't hard-code it.

A page can contain fewer events than limit, or none at all, while has_more is true. Events for families you didn't select, and updates to records that are no longer visible, are skipped, but the cursor still moves past them. Keep reading until has_more is false.

Every request counts against your organization's daily event-feed allowance, including after=now and empty pages. The allowance is separate from ordinary reads. Polling faster than retry_after_seconds uses it up without returning more data.

Cursors aren't consumed. If your process stops before it saves a new cursor, request the previous one again: you get the same events, so make sure applying an event twice is harmless.

Events​

Each event describes one change to one record:

FieldMeaning
event_idUnique ID of this event. Use it to ignore an event you've already applied.
schema_versionAlways 1 in this version.
event_typeThe family and kind of change, such as job.updated. See Event types.
resource.typecustomers, customer_locations, jobs or members.
resource.idID of the record that changed.
resource.revisionThe record's revision after this change.
changed_fieldsNames of the fields that changed, limited to fields this connection can read. Values aren't included.
tombstonetrue for *.deleted events.
observed_atWhen FieldTask recorded the change, in UTC.
published_atWhen the event was added to the feed, in UTC.

Events are returned in the order FieldTask added them to the feed. That is close to, but not always exactly, the order the changes happened, so use revision rather than position to decide which state is newer.

Revisions​

A record's revision starts at 1 and increases with every recorded change. It's the same number as the revision field in that record's REST responses. Gaps between the revisions you see are normal.

  • Ignore an event whose revision is less than or equal to the revision you've stored for that record.
  • After reading the current record, store the revision from the read response, not from the event.

Created, updated and deleted​

  • *.created: the record became visible through the API, either because it was created or because a deleted record was restored. changed_fields lists all of its fields.
  • *.updated: one or more fields changed. Updates to a record that isn't visible when you read the page are skipped; you'll receive its *.deleted event instead.
  • *.deleted: the record was deleted or is no longer visible through the API. tombstone is true and changed_fields is empty. Deleted events are always returned, so you can remove the record from your copy. A record can later be created again with a higher revision.

Recovery​

Two problems mean the cursor can never be used again. Both include "resync_required": true:

ProblemCause
410 history_expiredThe cursor is more than 30 days old, or the changes after it are no longer kept. The feed keeps changes for 30 days.
409 access_context_changedSince the cursor was issued, the connection's permissions changed, its access was otherwise changed, or FieldTask changed what the feed includes for your organization.

Don't retry the same cursor. Instead:

  1. Request after=now and keep the new cursor.
  2. Backfill again from the list endpoints. Replace your local copy, including removing records that no longer appear.
  3. Continue polling with the new cursor.

Other problems are recoverable without a resync:

  • 400 invalid_cursor: you sent a cursor that was modified, belongs to another connection or was issued for different resources. Send the right cursor with its own selection. If you've lost it, start again as above.
  • 429: wait for Retry-After seconds, then retry with the same cursor.
  • 503 sync_unavailable: retry with backoff using the same cursor. If it continues, ask FieldTask to check that sync is enabled for your organization.

Errors​

StatuscodeMeaningWhat to do
400invalid_queryThe query has an unknown or repeated parameter, after is longer than 4,096 characters, limit isn't a whole number from 1 to 100, or resources names an unknown family or repeats one.Send only after, resources and limit, each once.
400invalid_cursorafter is missing or empty, or the cursor was changed, belongs to another connection, or was issued for a different resources selection.Send the cursor exactly as returned, with the same resources it was issued for. If you've lost it, start again with after=now and backfill.
401invalid_tokenThe bearer token is missing, malformed, expired, revoked or issued for a different resource (for example an MCP sign-in).Send a current REST API token or REST OAuth access token as Authorization: Bearer ….
403insufficient_scopeA family in resources isn't available to this connection: FieldTask hasn't enabled it, your organization isn't enrolled for it, or the connection lacks its read permissions. Without resources, no family is available. error.code is missing_permission.Check the permissions in the table above, or remove the family from resources. Ask FieldTask whether the family is enabled for your organization.
404resource_not_foundFieldTask hasn't enabled the event feed in this environment, or the URL is wrong.Check the API origin and the path /public/v1/events (no trailing slash).
405method_not_allowedThis path doesn't accept the HTTP method, or the create isn't enabled in this environment. The Allow header lists the methods that are.Use a method listed in Allow.
409access_context_changedThe connection's permissions, enrollment or resource selection changed since this cursor was issued. resync_required is true.Get a new cursor with after=now, then rebuild the affected local data from the list endpoints.
410history_expiredThe cursor points to changes older than the feed keeps. resync_required is true.Get a new cursor with after=now, then rebuild your local data from the list endpoints.
429rate_limit_exceededToo many requests in the current minute, or too many concurrent requests, for the organization or connection.Wait for Retry-After seconds, then retry.
429daily_quota_exceededThe organization or connection has used its daily event-feed allowance. Every feed request counts, including after=now and empty pages. The allowance is separate from ordinary reads and resets at 00:00 UTC.Wait for Retry-After seconds. Poll no faster than page.retry_after_seconds.
503sync_unavailableThe feed can't be read right now, or change sync isn't set up for your organization or for a family you selected.Retry with backoff using the same cursor. If it continues, ask FieldTask to confirm that sync is enabled for your organization.