Developers
Everything you need to build an HR integration once
REST and JSON, one data model for every HR system, webhooks for changes, and an OpenAPI 3 reference you can generate clients from.
Concepts
The basics
-
API keys
Server-side requests use
Authorization: Bearer remic_live_…. Keys are shown once, stored only as hashes and can be revoked any time in the dashboard. -
Connections
One connection per client and HR system. Data requests name it with the
X-Connection-Idheader; you only ever see your own connections. -
Scopes
Your client consents per data category: employees, contact details, teams, locations, compensation, time off. Fields outside the granted scopes are never returned.
-
Pagination
Lists return
{ results, next }with an opaque cursor. UseupdatedAfterto fetch only what changed since your last read. -
Rate limits
Responses include
X-RateLimit-Limit,-Remainingand-Reset. Over the limit you get429withRetry-After. -
Errors
Every error has the same shape:
{ statusCode, code, message, details? }with a stable machine-readablecode.
Quickstart
From zero to employee data
Create a session on your server, open the popup in your frontend, read data and listen for changes.
-
1. Create a connect session
Server-side, with your API key. Pass your own ID for the client and the data scopes you need. You get a single-use URL that expires after 30 minutes.
curl -X POST https://api.remic.dev/v1/connect/sessions \ -H "Authorization: Bearer $REMIC_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "endUserOriginId": "client-42", "endUserName": "Bakery Schmidt GmbH", "scopes": ["employees.basic", "teams", "time_off"] }' # → { "sessionId": "…", "url": "https://connect…/?token=…", "expiresAt": "…" }const res = await fetch('https://api.remic.dev/v1/connect/sessions', { method: 'POST', headers: { Authorization: `Bearer ${process.env.REMIC_API_KEY}`, 'Content-Type': 'application/json', }, body: JSON.stringify({ endUserOriginId: 'client-42', endUserName: 'Bakery Schmidt GmbH', scopes: ['employees.basic', 'teams', 'time_off'], }), }); const { url } = await res.json(); // hand this to your frontend -
2. Open the connect popup
The SDK opens a hosted popup where your client picks their HR system, follows a setup guide, enters credentials and gives consent. Credentials never touch your systems. Results are only posted back to origins you allow in the dashboard.
import { openConnect } from '@remic/connect-sdk'; // url comes from your backend (POST /v1/connect/sessions) const { connectionId } = await openConnect(url); // Confirm server-side: GET /v1/connections/:id -
3. Read unified data
Employees, employments, teams, locations, time-off types, requests and balances, with your custom fields included.
curl "https://api.remic.dev/v1/hris/employees?pageSize=100" \ -H "Authorization: Bearer $REMIC_API_KEY" \ -H "X-Connection-Id: $CONNECTION_ID" # → { "results": [ …employees ], "next": "eyJjIjoi…" } # Pass ?cursor=<next> for the next page, ?updatedAfter=<ISO> for changes only.async function* employees(connectionId: string) { let cursor: string | null = null; do { const query = new URLSearchParams({ pageSize: '100', ...(cursor ? { cursor } : {}) }); const res = await fetch(`https://api.remic.dev/v1/hris/employees?${query}`, { headers: { Authorization: `Bearer ${process.env.REMIC_API_KEY}`, 'X-Connection-Id': connectionId, }, }); const page = await res.json(); yield* page.results; cursor = page.next; } while (cursor); } -
4. Listen for changes
Webhooks are signed with HMAC-SHA256 over the timestamp and raw body and retried for about 16 hours; you can replay up to 7 days of events. Change events carry IDs and field names only; fetch the current data from the API.
import crypto from 'node:crypto'; // header: X-Remic-Signature: t=<unix seconds>,v1=<hex> export function verify(rawBody: string, header: string, secret: string): boolean { const t = /t=(\d+)/.exec(header)?.[1]; const v1 = /v1=([0-9a-f]{64})/.exec(header)?.[1]; if (!t || !v1) return false; if (Math.abs(Date.now() / 1000 - Number(t)) > 300) return false; // replay window const expected = crypto.createHmac('sha256', secret).update(`${t}.${rawBody}`).digest('hex'); return crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(v1)); }{ "id": "evt_8KfQ2…", "type": "hris.employees.changed", "createdAt": "2026-10-27T09:12:44.120Z", "data": { "connectionId": "6957e4c8-…", "syncRunId": "47e872fb-…", "created": ["emp_3eIG9JE6kd2z1Spd"], "updated": [{ "id": "emp_14Mylj48go5gqDpt", "changedFields": ["jobTitle"] }], "deleted": [], "part": 1, "totalParts": 1 } }
Webhooks
Events
| Event | When |
|---|---|
| connection.created | A client finished the connect flow |
| connection.invalid_credentials | Credentials stopped working |
| connection.issue_detected / _resolved | Integration health changed |
| sync.finished | A sync run ended, with per-entity counts |
| hris.employees.changed | Created, updated (with field names) and deleted IDs |
| hris.employments / teams / locations / time_off_*.changed | Same for every other entity |
Passthrough
Data outside the unified model
Read-only access to the HR system’s own API through the connection. It requires the
passthrough scope, which is never requested by
default and is shown to your client as full read access before they consent.
# Requires the 'passthrough' scope, which your client must grant explicitly.
curl -X POST https://api.remic.dev/v1/passthrough \
-H "Authorization: Bearer $REMIC_API_KEY" \
-H "X-Connection-Id: $CONNECTION_ID" \
-H "Content-Type: application/json" \
-d '{ "method": "GET", "path": "/employees" }'
# → { "status": 200, "headers": { "content-type": "…" }, "body": { … } }