---
name: bindbee
description: Use when integrating with HR systems (HRIS), applicant tracking
  systems (ATS), or learning management systems (LMS). Reach for this skill when
  building connectors to read/write employee data, managing data syncs, handling
  webhooks, extending unified models with custom fields, or troubleshooting
  connector issues.
metadata:
  mintlify-proj: bindbee
  version: "1.0"
---

# Bindbee Skill

## Product summary

Bindbee is a unified API platform that normalizes data from multiple HR, recruiting, and learning systems into a single schema. Your application connects once against Bindbee's API; your customers authorize their own systems (BambooHR, Workday, ADP, Paycom, etc.) through a hosted link or embedded flow. Bindbee syncs their data on a schedule, normalizes it, and serves it through `/api/{category}/v1/*` endpoints where `category` is `hris`, `ats`, or `lms`. You read from synced data and write back to the source system in real time. Every request requires an API key (identifies your organization) and a connector token (identifies one customer's connection). See https://docs.bindbee.dev for the full documentation.

**Key files and endpoints:**
- API key: Settings → API Key (separate keys for Development and Production)
- Connector token: Found in connector's Overview tab or via `GET /api/v1/connectors`
- Base URL: `https://api.bindbee.dev/api/{category}/v1/`
- Categories: `hris` (employee data), `ats` (recruiting), `lms` (learning)

## When to use

Reach for this skill when:
- Building or debugging a connector to a customer's HR system
- Reading employee, candidate, or learning data through the unified API
- Writing data back (create employee, submit time-off, etc.)
- Setting up webhooks to react to sync events or data changes
- Extending the unified model with custom fields from raw upstream data
- Troubleshooting sync failures, missing records, or data discrepancies
- Configuring scoping to control which models and fields Bindbee requests
- Preparing for production launch (go-live checklist)

## Quick reference

### Authentication headers

| Header | Value | Required for |
|--------|-------|--------------|
| `Authorization` | `Bearer <BINDBEE_API_KEY>` | Every request |
| `X-Connector-Token` | `<CONNECTOR_TOKEN>` | Unified data reads/writes, passthrough, resync |
| `X-Idempotency-Key` | Unique string | POST /employees, /employee-payroll-runs, /time-off, /timesheet-entry (HRIS only) |

### Core concepts

| Term | Meaning |
|------|---------|
| **Category** | `hris`, `ats`, or `lms` — determines which models exist |
| **Integration** | Vendor system (e.g., `bamboohr`, `workday`) |
| **Connector** | One customer's authorized connection to one integration |
| **Model** | Unified object type (e.g., employee, candidate, benefit) |
| **Record** | One instance of a model from one connector |
| **Sync** | Scheduled job that pulls data from source and normalizes it |
| **Scope** | Organization-level setting: which models and fields to request |
| **Custom field** | Unmapped upstream value extracted via JMESPath and added to unified model |
| **id** | Bindbee's stable identifier (store this) |
| **remote_id** | Source system's own identifier |

### Common query parameters

| Parameter | Purpose | Example |
|-----------|---------|---------|
| `ids` | Filter by Bindbee IDs | `?ids=id1,id2,id3` |
| `remote_id` | Filter by source system ID | `?remote_id=emp123` |
| `employment_status` | Filter employees | `?employment_status=ACTIVE` |
| `modified_after` | Incremental sync | `?modified_after=2024-02-21T21:22:12.993Z` |
| `expand` | Inline related objects | `?expand=manager,work_locations` |
| `include_custom_fields` | Add custom field values | `?include_custom_fields=true` |
| `page_size` | Records per page (default 20, max 200) | `?page_size=100` |
| `cursor` | Pagination token | `?cursor=abc123` |

### Webhook events (Version 2)

| Event | Fires when |
|-------|-----------|
| `connector.linked` | Connector successfully authorized |
| `connector.relink_needed` | Credentials stopped working |
| `connector.relinked` | Credentials re-authorized |
| `connector.sync.started` | Sync begins |
| `connector.sync.completed` | Sync finishes successfully |
| `connector.sync.failed` | Sync fails |
| `{category}.{model}.created` | Records created in sync (e.g., `hris.employee.created`) |
| `{category}.{model}.updated` | Records updated in sync (e.g., `hris.employee.updated`) |

## Decision guidance

### When to use Development vs Production

| Use Development | Use Production |
|-----------------|----------------|
| Testing before go-live | Paying customer's live connection |
| Sandbox/demo tenant | Real customer data |
| Validating field mappings | Customer-facing data feeds |
| Manual resync needed | Syncs run on schedule unattended |

### When to read vs write

| Read | Write |
|------|-------|
| Fetch synced data from last completed sync | Send data straight to source system |
| Fast, consistent response | Reaches source immediately |
| Freshness limited by sync schedule | Failures are business failures (insufficient balance, closed period, etc.) |
| Use webhooks to detect changes | Not readable back until next sync picks it up |

### When to use filters vs expand vs modified_after

| Use filters | Use expand | Use modified_after |
|------------|-----------|-------------------|
| Narrow to specific records (employment_status, company_id) | Inline related objects (manager, locations) | Incremental sync since last timestamp |
| Reduces response size | Increases response size | Avoids re-processing unchanged records |
| Combine with other parameters | Reduce page_size when using | Gate on sync-completed event |

### When to use custom fields vs passthrough

| Custom fields | Passthrough |
|---------------|------------|
| Extend unified model with unmapped upstream value | Call source system's own API directly |
| Mapped via JMESPath expression | Use when unified model doesn't cover endpoint |
| Persisted in Bindbee | No idempotency key support |
| Queryable in responses | One-off requests |

## Workflow

### 1. Set up a connector (Development first)

1. Go to app.bindbee.dev → Connectors → Development tab
2. Click **+ Test Connector**
3. Select category (HRIS, ATS, or LMS) and integration (e.g., BambooHR)
4. Enter Unique Customer ID (your internal tenant ID, stored as `origin_id`)
5. Follow authorization prompts (OAuth or API key, depending on integration)
6. Copy the `connector_token` from the connector's Overview tab
7. Wait for initial sync to complete (Status reads "Synced")

### 2. Read data from a connector

1. Identify the model you need (employee, candidate, benefit, etc.)
2. Construct the endpoint: `GET /api/{category}/v1/{model_plural}`
3. Add headers: `Authorization: Bearer <API_KEY>`, `X-Connector-Token: <TOKEN>`
4. Add query parameters: filters, expand, modified_after, page_size
5. Paginate using cursor until `cursor == null`
6. Handle nullable fields and unmapped enum values with defaults

### 3. Write data back to source system

1. Call the Meta API endpoint to fetch the schema for that connector: `GET /api/{category}/v1/{model}/meta`
2. Build request body using required fields from the schema (varies by integration)
3. Add `X-Idempotency-Key` header (HRIS write endpoints only)
4. POST to the write endpoint
5. Handle business failures (insufficient balance, closed period, etc.) from source system
6. Force a resync to confirm the write appears in reads

### 4. Set up webhooks

1. Go to Webhooks in the dashboard
2. Click **Add webhook**
3. Enter destination URL (must be HTTPS)
4. Click **Send test event** to verify reachability
5. Leave API Version on **Version 2** (Version 1 deprecated Dec 31, 2026)
6. Select environments (Production, Development, or both)
7. Select events (subscribe only to what you process)
8. Create webhook
9. Verify signatures on every incoming request using Standard Webhooks library

### 5. Extend the unified model with custom fields

1. Create a custom field in the dashboard or via API: `POST /api/v1/custom-fields`
2. Write a JMESPath expression to extract the value from raw_data
3. Test the expression: `POST /api/v1/custom-fields/preview` (dry-run)
4. Create a mapping: `POST /api/v1/custom-fields/mapping` (organization-scoped or connector-scoped)
5. Add `include_custom_fields=true` to any unified read request
6. Value appears in response or as "INVALID_JSON_PATH" if expression fails

### 6. Troubleshoot a failing connector

1. Check sync status: Open connector → Overview tab → Sync panel
2. If Status is "Failed", expand the Issue to see which models failed
3. Match the pattern: one model family (all payroll) suggests permission gap; different models on different runs suggests transient error
4. For transient errors, click **Force Sync**
5. For permission gaps, customer's admin must grant access upstream
6. For "RELINK_NEEDED", click **Relink Connector** and send updated link to customer
7. Check logs: Logs tab shows request/response pairs for debugging

## Common gotchas

- **Pagination stops on `cursor == null`, not on a short page.** Terminating early will miss records.
- **Reads answer from the last completed sync, not real-time.** A record edited an hour ago is invisible until the next sync. Use webhooks to detect changes instead of polling.
- **Writes are not readable back immediately.** They land in the source system but reads still answer from the last sync. Force a resync to confirm sooner.
- **A write that times out can still reach the provider.** Retrying blind creates duplicates. Always send `X-Idempotency-Key` on HRIS writes.
- **Connector tokens are scoped to one category.** An HRIS token used against `/api/ats/v1/*` returns 403 even though both credentials are valid.
- **Custom field JMESPath errors don't raise exceptions.** Invalid expressions return "INVALID_JSON_PATH" in the response. Test with the preview endpoint first.
- **Scope changes trigger a resync.** Widening scope later re-reads the entire population. Settle scope before connecting real customers.
- **Enum values are not closed sets.** Unmapped vendor values pass through verbatim. Always include a default case in switches.
- **Webhook retries stop after ~60 seconds.** Longer outages are lost silently. Don't rely on webhooks for critical alerting.
- **Development connectors don't sync on a schedule.** Only the first sync runs automatically. Use Force Sync or call the resync endpoint for subsequent runs.
- **Stale data doesn't announce itself.** A connector that stopped syncing weeks ago still returns 200. Monitor sync status separately.

## Verification checklist

Before submitting work:

- [ ] API key is from the correct environment (Development or Production)
- [ ] Connector token is for the correct category (HRIS, ATS, or LMS)
- [ ] Pagination loop exits on `cursor == null`, not on empty items or short page
- [ ] All enum fields have a default case for unmapped values
- [ ] Writes include `X-Idempotency-Key` header (HRIS endpoints only)
- [ ] Write request body comes from Meta API schema, not hardcoded
- [ ] Custom field JMESPath tested with preview endpoint before saving
- [ ] Webhook signatures verified using Standard Webhooks library
- [ ] Sync status monitored separately from read responses (stale data doesn't error)
- [ ] Connector relink path exists for `RELINK_NEEDED` state
- [ ] Rate limit retries honor `Retry-After` header
- [ ] 5xx errors retried with exponential backoff; 4xx errors never retried
- [ ] origin_id is stable internal tenant ID, not name or email
- [ ] Token exchange runs server-side, never in browser

## Resources

- **Full documentation:** https://docs.bindbee.dev/llms.txt (comprehensive page-by-page navigation for agents)
- **API Reference:** https://docs.bindbee.dev/api-reference/basics/authentication
- **Reading & Writing Data:** https://docs.bindbee.dev/guides/reading-writing/overview
- **Webhooks:** https://docs.bindbee.dev/guides/webhooks/overview
- **Go-Live Checklist:** https://docs.bindbee.dev/guides/go-live-checklist

---

> For additional documentation and navigation, see: https://docs.bindbee.dev/llms.txt