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 pollGET /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
- Do one initial load with
GET /v1/employments, paginating through every page. - Subscribe to employment change webhooks. See Subscribing to webhooks and Verifying webhooks for the mechanics.
- 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, callGET /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 includeactive,initiated,invited,pending,review,offboarding,archived, anddeleted. Also accepts the special valueincompletefor employments that are not onboarded yet.employment_model: one ofeor,peo, orglobal_payroll.employment_type: the employment product type, for exampleemployee,contractor,direct_employee, orglobal_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.updatedemployment.details.updatedemployment.personal_information.updatedemployment.administrative_details.updatedemployment.status.updatedemployment.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
| Job | Where to look |
|---|---|
Seed the initial load. Paginate with page and page_size; filter by status, employment_model, company_id | GET /v1/employments |
| Subscribe to employment change events so you receive deltas | POST /v1/webhook-callbacks |
| Full record for a single employment. Use for a one-off lookup, not bulk sync; do not call in a loop | GET /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_pagetototal_pagesand 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 withGET /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 untilcurrent_pageequalstotal_pages), then subscribe to employment change webhooks viaPOST /v1/webhook-callbacksand upsert deltas byemployment_id. Never pollGET /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
- Subscribing to webhooks and Verifying webhooks: set up and secure the change stream.
- Manage Employment Contracts Changes: the same pattern for versioned contract terms.
Updated 4 days ago