Sync your workforce data efficiently

Keep your systems in sync with Remote's workforce state the efficient way: load the full list once, then let webhooks push you the changes. Do not poll one employee at a time.

👥

Who this is for. Integration and data teams mirroring Remote's workforce into an HRIS, data warehouse, IdP, provisioning system, or internal directory. If you run a loop over employee IDs today to refresh your copy, this guide is for you.

Keep your systems in sync with Remote's workforce state the efficient way: load the full list once, then let change events push you the deltas.

🚩

Read this first (anti-pattern). Do not poll GET /v1/employments/{employment_id} in a loop, one call per employee. That is the slow, expensive way to stay in sync, and it is the single most common mistake we see. About 80% of active customers read workforce state, and per-employee polling is what drives the load. One customer alone ran roughly 8.8M calls per month fetching employees one at a time. The pattern below does the same job with a fraction of the calls.

The model nuance

Workforce state in Remote is one resource: an employment. You do not rebuild that resource employee by employee. You read the list once as a seed, then apply change events as they arrive. Think of it as snapshot plus stream, not re-query on a timer.

This holds across surfaces. Whether you reach employments through the API, an SDK, the CLI, or an MCP tool, you are reading the same employment resource. Only the way you fetch it differs. The efficient sync pattern is the same everywhere: seed from the list, keep current from events.

Before you begin

  • A Remote API token with access to employments. Send it as Authorization: Bearer $REMOTE_API_TOKEN. For step 3, the token must be company-scoped, since webhooks always belong to one company.
  • A publicly reachable HTTPS endpoint that can receive webhook callbacks.
  • A place to store your local copy of workforce state, keyed by employment_id.

See Environment setup for the base host to use per environment.

The approach

  1. Do one initial load with GET /v1/employments, paginating through every page.
  2. Subscribe to employment change webhooks. See Subscribing to webhooks and Verifying webhooks for the mechanics.
  3. Apply each incoming event to your local copy so it stays current without re-fetching.

1. Load the full list

Start at page 1 and pull a full page at a time.

curl -s "https://gateway.remote.com/v1/employments?page=1&page_size=100" \
  -H "Authorization: Bearer $REMOTE_API_TOKEN"

The response wraps the list plus pagination metadata:

{
  "data": {
    "employments": [
      {
        "id": "...",
        "full_name": "...",
        "job_title": "...",
        "status": "active",
        "type": "employee",
        "employment_model": "eor",
        "...": "lightweight employment, see note below"
      }
    ],
    "current_page": 1,
    "total_count": 1287,
    "total_pages": 13
  }
}

Keep requesting pages until current_page equals total_pages. Use total_count as a sanity check on how many records you expect.

ℹ️

The list returns a lightweight employment: id, full name, job title, status, type, emails, department, employment_model, external_id, and lifecycle stage. It does not include full onboarding details or country-specific form data. When you need the complete record for one employee, call GET /v1/employments/{employment_id}. See the API Reference for the full field list.

2. Narrow the load with filters (optional)

You can filter the list so you only seed what you need. GET /v1/employments accepts these query filters:

  • status: one or more status values, comma-separated. Values include active, initiated, invited, pending, review, offboarding, archived, and deleted. Also accepts the special value incomplete for employments that are not onboarded yet.
  • employment_model: one of eor, peo, or global_payroll.
  • employment_type: the employment product type, for example employee, contractor, direct_employee, or global_payroll_employee.
  • company_id: scope to a single company.
  • email: match by login email.
  • short_id: match by short employment id. Returns at most one result.
curl -s "https://gateway.remote.com/v1/employments?status=active&employment_model=eor&page=1&page_size=100" \
  -H "Authorization: Bearer $REMOTE_API_TOKEN"

3. Subscribe to employment change webhooks

Register a callback with POST /v1/webhook-callbacks so Remote pushes you employment changes instead of you re-fetching. The body takes the callback url plus either a subscribed_events array or subscribe_to_all_events: true.

There is no single "employment changed" event. Subscribe to the employment.* events that matter for your copy of the data, for example:

  • employment.updated
  • employment.details.updated
  • employment.personal_information.updated
  • employment.administrative_details.updated
  • employment.status.updated
  • employment.work_email.updated
curl -s -X POST "https://gateway.remote.com/v1/webhook-callbacks" \
  -H "Authorization: Bearer $REMOTE_API_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://your-app.example.com/webhooks/remote",
    "subscribed_events": [
      "employment.updated",
      "employment.details.updated",
      "employment.personal_information.updated"
    ]
  }'

For the full event catalog see Available Webhooks. For the request body, signature verification, and delivery model, follow Subscribing to webhooks and Verifying webhooks.

4. Apply deltas to your local copy

When a change event arrives, upsert that employment into your store by employment_id. Your copy now stays current between full loads, and you never loop over individual employees to refresh.

Key capabilities

JobWhere to look
Seed the initial load. Paginate with page and page_size; filter by status, employment_model, company_idGET /v1/employments
Subscribe to employment change events so you receive deltasPOST /v1/webhook-callbacks
Full record for a single employment. Use for a one-off lookup, not bulk sync; do not call in a loopGET /v1/employments/{employment_id}

Gotchas & limits

  • The list is the seed, events are the stream. Do the full paginated load once, then rely on webhooks for changes. Re-listing on a timer is not the sync mechanism.
  • Do not loop per employee. GET /v1/employments/{employment_id} is for single lookups. Fanning it out across your whole workforce is the exact anti-pattern this guide replaces.
  • Page all the way through. Compare current_page to total_pages and keep going until they match.
  • The list is lightweight. It carries identity, status, type, and employment_model, not full onboarding or country-specific data. Use it to know who exists and what changed. Fetch the full record for one employee with GET /v1/employments/{employment_id} only when you actually need those extra fields, not as a routine sweep.
  • Reconcile occasionally. If your endpoint was down or an event was missed, an infrequent full re-list (step 1) is a safe way to catch up. Keep it a backstop, not the primary loop.
  • Contract changes have their own events. To track salary, title, and other contract term changes specifically, see Manage Employment Contracts Changes, which applies the same listen-then-fetch pattern to versioned contracts.

🤖 For AI agents

Point your assistant (or the Remote MCP server) at the API Reference with this plan:

To sync Remote workforce data, do one paginated load of GET /v1/employments (page through until current_page equals total_pages), then subscribe to employment change webhooks via POST /v1/webhook-callbacks and upsert deltas by employment_id. Never poll GET /v1/employments/{employment_id} in a loop.

The machine-readable spec for these endpoints is in llms-full.txt, so an agent can read the exact paths, params, and response shapes directly.

Next steps


Did this page help you?