# Authentication Source: https://docs.bindbee.dev/api-reference/basics/authentication Learn about the process of authenticating your requests to the Bindbee API. ## Overview When initiating requests to the Bindbee API, it is essential to include the necessary authentication parameters to establish your identity as an authorized user. There are two primary authentication protocols: 1. Bindbee API Key 2. Connector Tokens Credentials obtained through these protocols must be incorporated into the headers of each request sent to the Bindbee API. ## Bindbee API Key For any communication with the Bindbee API, it is crucial to have an API key to authenticate yourself as an authorized user. It is recommended to securely store your access key after its creation in Bindbee In case you no longer have access to your key, you have the option to regenerate you Bindbee API Key in [API Key](https://app.bindbee.dev/settings/keys) Tab in Settings. Regenerate Token When creating your own requests, it is essential to include your API key with a **"Bearer "** prefix as a header named `Authorization`. This practice is crucial for authorizing your Bindbee API requests effectively. Ensure that this specific header is incorporated in every request, adhering to the following format: `Authorization: Bearer YOUR_BINDBEE_API_KEY` ## Connector Tokens When issuing requests to the Bindbee API concerning your end users' data, access or manipulation, authorization is granted only if the users have undergone the [Bindbee's Magic Link](/features/magic-link) process and successfully stored their `connector_token`, as this token is a prerequisite for the successful execution of such requests. You can find your `connector_token` inside the **Overview** section of your connector Connector Token When creating your own requests, it is essential to include your your user's `connector_token` as a header named `X-Connector-Token` for authorizing your Bindbee API requests effectively. Ensure that this specific header is incorporated in every request, adhering to the following format: `X-Connector-Token: END_USER_CONNECTOR_TOKEN` # Pagination Source: https://docs.bindbee.dev/api-reference/basics/pagination get /api/hris/v1/employees Learn about the process of implementing pagination for handling bulk data requests to the Unified API. ## Overview Any data you interact with via the Bindbee API can be paginated, and this pagination is determined by the `cursor` and `page_size` query parameters. ## Sample HTTP Request Above is an example of an interactive HTTP request using the Bindbee API to demonstrate how pagination works: # Rate Limits Source: https://docs.bindbee.dev/api-reference/basics/rate-limits Understanding and managing API rate limits in Bindbee ## Overview Bindbee enforces rate limits to ensure fair usage and maintain service reliability for all customers. All API endpoints are subject to rate limiting to protect our infrastructure and provide consistent performance. ## Rate Limit Policy **Current Rate Limit:** **200** requests **per minute** per connector The Bindbee API enforces rate limits based on the **connector** (as indicated by the `connector_token` in the request header). This means each of your end users has their own independent rate limit of 200 requests per minute. This rate limit applies across all endpoints and is enforced strictly. Once a connector reaches the limit, subsequent requests for that connector will be rejected with a `429 Too Many Requests` error until the rate limit window resets. Rate limits are applied **per connector token**, not per API key. This ensures fair resource allocation across all your end users' connections. ## Rate Limit Headers Bindbee includes rate limit information in the response headers of every API request, allowing you to monitor your usage and implement appropriate backoff strategies. ### Successful Requests (2xx Status) When your request is successful, the following headers are included: | Header | Description | Example | | ----------------------- | ---------------------------------------------------------- | ------------ | | `X-RateLimit-Limit` | The maximum number of requests allowed per minute | `200` | | `X-RateLimit-Remaining` | The number of requests remaining in the current window | `157` | | `X-RateLimit-Reset` | Unix timestamp (in seconds) when the rate limit will reset | `1700000000` | **Example Response Headers:** ``` X-RateLimit-Limit: 200 X-RateLimit-Remaining: 157 X-RateLimit-Reset: 1700000000 ``` ### Rate Limit Exceeded (429 Status) When you exceed the rate limit, you'll receive a `429 Too Many Requests` response with the following headers: | Header | Description | Example | | ----------------------- | ------------------------------------------------- | ------------ | | `X-RateLimit-Limit` | The maximum number of requests allowed per minute | `200` | | `X-RateLimit-Remaining` | Always `0` when rate limited | `0` | | `X-RateLimit-Reset` | Unix timestamp (in seconds) when you can retry | `1700000060` | | `Retry-After` | Number of seconds to wait before retrying | `60` | **Example 429 Response:** ```json theme={null} HTTP/1.1 429 Too Many Requests X-RateLimit-Limit: 200 X-RateLimit-Remaining: 0 X-RateLimit-Reset: 1700000060 Retry-After: 60 { "error": "Rate limit exceeded", "message": "You have exceeded the rate limit of 200 requests per minute" } ``` ## Best Practices ### 1. Monitor Rate Limit Headers Always check the `X-RateLimit-Remaining` header to track your usage and adjust your request frequency accordingly. ### 2. Implement Retry Logic with Backoff When you receive a `429` response, wait for the duration specified in the `Retry-After` header before retrying. ### 3. Distribute Requests Across Multiple Connectors Since rate limits are per connector, requests for different end users (different connector tokens) have independent rate limits. You can process multiple connectors in parallel without affecting each other's rate limits. ### 4. Batch Requests When Possible Reduce the number of API calls by using batch endpoints or requesting multiple resources in a single call where supported. ### 5. Implement Rate Limit Tracking Track rate limit usage across your application to proactively avoid hitting limits. ## Calculating Time Until Reset The `X-RateLimit-Reset` header contains a Unix timestamp. You can calculate the wait time as follows: ```python theme={null} import time from datetime import datetime reset_timestamp = int(response.headers.get('X-RateLimit-Reset')) current_time = int(time.time()) seconds_until_reset = reset_timestamp - current_time print(f"Rate limit resets in {seconds_until_reset} seconds") print(f"Reset time: {datetime.fromtimestamp(reset_timestamp)}") ``` ## Need Higher Limits? If your application requires higher rate limits for specific connectors or use cases, please contact our support team at [support@bindbee.dev](mailto:support@bindbee.dev) or reach out to your account manager. We offer custom rate limits for enterprise customers based on your specific needs. ## Common Questions ### What happens if I exceed the rate limit? Your requests for that specific connector will be rejected with a `429 Too Many Requests` status code until the rate limit window resets (60 seconds from when you first exceeded the limit). ### Does the rate limit apply per endpoint or across all endpoints? The rate limit applies across all endpoints for a given connector. All requests made with the same `X-Connector-Token` count toward the same 200 requests per minute limit. ### Are rate limits shared across different connector tokens? No, each connector token has its own independent rate limit of 200 requests per minute. This means different end users (different connectors) do not affect each other's rate limits. ### Can I make requests without a connector token? Some endpoints may not require a connector token (such as organization-level endpoints), but any endpoint that accesses or manipulates end user data requires both the API key and the connector token. Check the specific endpoint documentation for requirements. # Sync Frequency Source: https://docs.bindbee.dev/api-reference/basics/sync-frequency Learn about the synchronization frequency for importing data from external sources into Bindbee. ## Overview Sync frequency represents the pace at which Bindbee retrieves data from your third-party integrations. The data synchronization occurs **once every 24 hours** by default, and this timeframe is adjustable based on customization preferences. # Delete Connector Source: https://docs.bindbee.dev/api-reference/connectors/delete-connector delete /api/hris/v1/connectors/{connector_id}/delete Deletes the provided connector ## Overview This is a demo of how to delete the Connector for specific category. This guide will help you understand the endpoints available and how to utilize them effectively. You can also access connector for different categories using `https://api.bindbee.dev/api/{category}/v1/connectors/{connector_id}/delete`. To delete ATS connector, use: `https://api.bindbee.dev/api/ats/v1/connectors/ To delete LMS connector, use: `[https://api.bindbee.dev/api/lms/v1/connectors/](https://api.bindbee.dev/api/lms/v1/connectors/) /delete\` # Get Connectors Source: https://docs.bindbee.dev/api-reference/connectors/get-connectors get /api/hris/v1/connectors Returns a list of HRIS connectors ## Overview This is a demo of how to use the Connectors API to get all connectors for specific category. This guide will help you understand the endpoints available and how to utilize them effectively. You can also access connectors for different categories using `https://api.bindbee.dev/api/{category}/v1/connectors`. To retrieve ATS connectors, use: `https://api.bindbee.dev/api/ats/v1/connectors` and to retrieve LMS connectors, use: `https://api.bindbee.dev/api/lms/v1/connectors` # Force Resync a Connector Source: https://docs.bindbee.dev/api-reference/connectors/resync-connector post /api/embedded/v1/connectors/resync ## Overview This guide demonstrates how to use the Resync API to force a sync of a connector. It is neither necessary nor recommended to call this endpoint periodically. Bindbee automatically performs periodic syncs for you. You should only trigger a resync in specific cases, such as when a user manually initiates a sync from your application (e.g., by clicking a “Sync” button). # Get Org Integrations Source: https://docs.bindbee.dev/api-reference/integrations/get-integrations get /api/hris/v1/integrations Returns a list of Integrations ## Overview This is a demo of how to use the Integrations API to get all active integrations for specific category. This guide will help you understand the endpoints available and how to utilize them effectively. You can also access integrations for different categories using `https://api.bindbee.dev/api/{category}/v1/integrations`. To retrieve ATS integrations, use: `https://api.bindbee.dev/api/ats/v1/integrations` To retrieve LMS integrations, use: `https://api.bindbee.dev/api/lms/v1/integrations` # Make Passthrough Request Source: https://docs.bindbee.dev/api-reference/passthrough/make-passthrough-request post /api/v1/passthrough # Create Activity Source: https://docs.bindbee.dev/ats/activity/create-activity post /api/ats/v1/activities Creates a Activity object with the given values. # Get Activities Source: https://docs.bindbee.dev/ats/activity/get-activities get /api/ats/v1/activities Returns a list of Activity objects. # Get Activity By Id Source: https://docs.bindbee.dev/ats/activity/get-activity-by-id get /api/ats/v1/activities/{id} Returns a Activity object with the given id. # Create Application Source: https://docs.bindbee.dev/ats/application/create-application post /api/ats/v1/applications Creates an Application object with the given values. # Get Application By Id Source: https://docs.bindbee.dev/ats/application/get-application-by-id get /api/ats/v1/applications/{id} Returns an Application object with the given id. # Get Applications Source: https://docs.bindbee.dev/ats/application/get-applications get /api/ats/v1/applications Returns a list of Application objects. # Create Attachment Source: https://docs.bindbee.dev/ats/attachment/create-attachment post /api/ats/v1/attachments Creates an Attachment object with the given values. # Get Attachment By Id Source: https://docs.bindbee.dev/ats/attachment/get-attachment-by-id get /api/ats/v1/attachments/{id} Returns an Attachment object with the given id. # Get Attachments Source: https://docs.bindbee.dev/ats/attachment/get-attachments get /api/ats/v1/attachments Returns a list of Attachment objects. # Create Candidate Source: https://docs.bindbee.dev/ats/candidate/create-candidate post /api/ats/v1/candidates Creates a Candidate object with the given values. To create an candidate in ATS, you first need to fetch the request body structure that ATS expects. This structure varies based on the ATS provider (e.g., Greenhouse, Teamtailor, BambooHR). The request body consists of three parts: * **Model Keys**: Standard attributes required for all candidate. * **Additional Attributes**: Specific fields required by the chosen ATS. * **Custom Fields**: Extra metadata for specific implementations. **Note:** Some attributes may have `allowed_values`, meaning only specific values are permitted based on the customer's ATS configuration. Ensure you check for these constraints before submitting the request. ## Step 1: Fetch the Required Request Body Before calling the **Create Candidate** API, you need to get the request structure using the [Meta Create Candidate](/ats/candidate/meta-create-candidate) API. ## Step 2: Create an Candidate Once you have the request body structure, use the **Create Candidate** API to send the request. # Get Candidate By Id Source: https://docs.bindbee.dev/ats/candidate/get-candidate-by-id get /api/ats/v1/candidates/{id} Returns a Candidate object with the given id. # Get Candidates Source: https://docs.bindbee.dev/ats/candidate/get-candidates get /api/ats/v1/candidates Returns a list of Candidate objects. # Get Create Candidate Request Body Source: https://docs.bindbee.dev/ats/candidate/meta-create-candidate get /api/ats/v1/candidates/create/meta Returns the data points required to add new employee in HRIS # Create Candidate Attachment Source: https://docs.bindbee.dev/ats/candidate/write-attachment-for-existing-candidate post /api/ats/v1/candidates/{candidate_id}/attachments Uploads an attachment for a given candidate. Currently this functionality is available for Success Factor and Teamtailor 🚨 **Note:** File size cannot exceed **5 MB**. ### **For SAP Success Factor Integration** If integrating with **SAP\_SUCCESS\_FACTOR**, the request body must only contain: | Key | Type | Required | Description | | ---------------- | --------------- | ------------ | ---------------------------------------- | | `file_name` | string | **required** | Name of the file including its extension | | `file_content` | string (base64) | **required** | Base64 encoded file content | | `remote_user_id` | UUID | **required** | UUID of remote user created by Bindbee | ### **For Teamtailor Integration** If integrating with **Teamtailor**, the request body must only contain: | Key | Type | Required | Description | | ---------------- | ------ | ------------ | ---------------------------------------- | | `file_name` | string | **required** | Name of the file including its extension | | `file_url` | string | **required** | Publically accessible file url | | `remote_user_id` | UUID | **required** | UUID of remote user created by Bindbee | # Create Department Source: https://docs.bindbee.dev/ats/department/create-department post /api/ats/v1/departments Creates a Department object with the given values. # Get Department By Id Source: https://docs.bindbee.dev/ats/department/get-department-by-id get /api/ats/v1/departments/{id} Returns a Department object with the given id. # Get Departments Source: https://docs.bindbee.dev/ats/department/get-departments get /api/ats/v1/departments Returns a list of Department objects. # Create Eeoc Source: https://docs.bindbee.dev/ats/eeoc/create-eeoc post /api/ats/v1/eeocs Creates an EEOC record with the given values. # Get Eeoc By Id Source: https://docs.bindbee.dev/ats/eeoc/get-eeoc-by-id get /api/ats/v1/eeocs/{id} Returns an EEOC record with the given id. # Get Eeocs Source: https://docs.bindbee.dev/ats/eeoc/get-eeocs get /api/ats/v1/eeocs Returns a list of EEOC records. # Create Job Interview Stage Source: https://docs.bindbee.dev/ats/job-interview-stage/create-job-interview-stage post /api/ats/v1/job-interview-stages Creates a Job Interview Stage object with the given values. # Get Job Interview Stage By Id Source: https://docs.bindbee.dev/ats/job-interview-stage/get-job-interview-stage-by-id get /api/ats/v1/job-interview-stages/{id} Returns a Job Interview Stage object with the given id. # Get Job Interview Stages Source: https://docs.bindbee.dev/ats/job-interview-stage/get-job-interview-stages get /api/ats/v1/job-interview-stages Returns a list of Job Interview Stage objects. # Create Job Source: https://docs.bindbee.dev/ats/job/create-job post /api/ats/v1/jobs Creates a Job object with the given values. This endpoint creates a Job in the connected ATS via Bindbee's unified **Job** model. ## Provider-specific behavior Different ATS providers can have slightly different requirements and behaviors when creating jobs. Use the accordions below to see details for each provider.

When the underlying ATS is Greenhouse, a job can be created using a payload like:

```json theme={null} { "remote_user_id": "019a2903-74af-78eb-8beb-d9bfce1895c7", "template_id": "4033879009", "name": "Sales Manager", "departments": [ "019a1041-57f8-752d-bd75-6f5136196765" ], "offices": [ "019a1041-5980-724f-a669-9e904be609de", "019a1041-5980-7d5f-ae13-0eab242cfafc" ], "openings": 1 } ``` ### Required fields For Greenhouse, the following fields are required when creating a job through Bindbee: * **template\_id** * **remote\_user\_id** * **openings** All IDs (`remote_user_id`, `departments`, `offices`) should be the Bindbee model IDs returned from the respective unified endpoints, not raw Greenhouse IDs. ### How to get `template_id` 1. Fetch jobs from the Greenhouse connector using the Bindbee Jobs list endpoint. 2. In the raw\_data of each job, look for entries where is\_template is true. 3. Use either the id or remote\_id from that record as the template\_id when creating a job. ### Greenhouse-specific notes * Single department only: Greenhouse allows only one department per job. Even though the Bindbee model accepts an array in departments, you should send at most one department ID for Greenhouse. * Multiple offices allowed: You can associate multiple offices with a job by passing multiple Bindbee office IDs in the offices array. * Use unified IDs: Always use IDs from Bindbee's unified models (e.g., Departments, Offices, Remote Users) so that the same payload structure works consistently across ATS providers.
# Get Job By Id Source: https://docs.bindbee.dev/ats/job/get-job-by-id get /api/ats/v1/jobs/{id} Returns a Job object with the given id. # Get Jobs Source: https://docs.bindbee.dev/ats/job/get-jobs get /api/ats/v1/jobs Returns a list of Job objects. # Create Offer Source: https://docs.bindbee.dev/ats/offer/create-offer post /api/ats/v1/offers Creates an Offer object with the given values. # Get Offer By Id Source: https://docs.bindbee.dev/ats/offer/get-offer-by-id get /api/ats/v1/offers/{id} Returns an Offer object with the given id. # Get Offers Source: https://docs.bindbee.dev/ats/offer/get-offers get /api/ats/v1/offers Returns a list of Offer objects. # Create Office Source: https://docs.bindbee.dev/ats/office/create-office post /api/ats/v1/offices Creates an Office object with the given values. # Get Office By Id Source: https://docs.bindbee.dev/ats/office/get-office-by-id get /api/ats/v1/offices/{id} Returns an Office object with the given id. # Get Offices Source: https://docs.bindbee.dev/ats/office/get-offices get /api/ats/v1/offices Returns a list of Office objects. # Create Reject Reason Source: https://docs.bindbee.dev/ats/reject-reason/create-reject-reason post /api/ats/v1/reject-reasons Creates a RejectReason object with the given values. # Get Reject Reason By Id Source: https://docs.bindbee.dev/ats/reject-reason/get-reject-reason-by-id get /api/ats/v1/reject-reasons/{id} Returns a RejectReason object with the given id. # Get Reject Reasons Source: https://docs.bindbee.dev/ats/reject-reason/get-reject-reasons get /api/ats/v1/reject-reasons Returns a list of RejectReason objects. # Create Remote User Source: https://docs.bindbee.dev/ats/remote-user/create-remote-user post /api/ats/v1/remote-users Creates a RemoteUser object with the given values. # Get Remote User By Id Source: https://docs.bindbee.dev/ats/remote-user/get-remote-user-by-id get /api/ats/v1/remote-users/{id} Returns a RemoteUser object with the given id. # Get Remote Users Source: https://docs.bindbee.dev/ats/remote-user/get-remote-users get /api/ats/v1/remote-users Returns a list of RemoteUser objects. # Create Scheduled Interview Source: https://docs.bindbee.dev/ats/scheduled-interview/create-scheduled-interview post /api/ats/v1/scheduled-interviews Creates a ScheduledInterview object with the given values. # Get Scheduled Interview By Id Source: https://docs.bindbee.dev/ats/scheduled-interview/get-scheduled-interview-by-id get /api/ats/v1/scheduled-interviews/{id} Returns a ScheduledInterview object with the given id. # Get Scheduled Interviews Source: https://docs.bindbee.dev/ats/scheduled-interview/get-scheduled-interviews get /api/ats/v1/scheduled-interviews Returns a list of ScheduledInterview objects. # Create Scorecard Source: https://docs.bindbee.dev/ats/scorecard/create-scorecard post /api/ats/v1/scorecards Creates a Scorecard object with the given values. # Get Scorecard By Id Source: https://docs.bindbee.dev/ats/scorecard/get-scorecard-by-id get /api/ats/v1/scorecards/{id} Returns a Scorecard object with the given id. # Get Scorecards Source: https://docs.bindbee.dev/ats/scorecard/get-scorecards get /api/ats/v1/scorecards Returns a list of Scorecard objects. # Create Screening Question Source: https://docs.bindbee.dev/ats/screening-question/create-screening-question post /api/ats/v1/screening-questions Creates a ScreeningQuestion object with the given values. # Get Screening Question By Id Source: https://docs.bindbee.dev/ats/screening-question/get-screening-question-by-id get /api/ats/v1/screening-questions/{id} Returns a ScreeningQuestion object with the given id. # Get Screening Questions Source: https://docs.bindbee.dev/ats/screening-question/get-screening-questions get /api/ats/v1/screening-questions Returns a list of ScreeningQuestion objects. # Create Tag Source: https://docs.bindbee.dev/ats/tag/create-tag post /api/ats/v1/tags Creates a Tag object with the given values. # Get Tag By Id Source: https://docs.bindbee.dev/ats/tag/get-tag-by-id get /api/ats/v1/tags/{id} Returns a Tag object with the given id. # Get Tags Source: https://docs.bindbee.dev/ats/tag/get-tags get /api/ats/v1/tags Returns a list of Tag objects. # API Configuration Source: https://docs.bindbee.dev/custom-fields/api-workflow Define Custom Fields and mappings programmatically. Useful for onboarding automation, replicating configuration across environments, or syncing Bindbee with your internal admin tooling. The Custom Fields API exposes the same capabilities as the dashboard so you can define fields and mappings programmatically. For the concepts (what Custom Fields are, organization vs connector scope) see the [Custom Fields overview](/custom-fields/overview). For the dashboard equivalent, see [Dashboard Configuration](/custom-fields/dashboard). Fields created from the API are tagged with `source: "API"`. Fields created from the dashboard are tagged with `source: "DASHBOARD"`. Both kinds coexist and behave identically once created. ## Authentication All Custom Fields endpoints are scoped to your organization and require a Bearer token: ```http theme={null} Authorization: Bearer Content-Type: application/json ``` ## Concepts * **Custom field** — a named extension on a unified model (e.g. `guardian_mobile` on `employee`). It belongs to a `(category, model)` pair such as `(HRIS, employee)`. * **Mapping** — the rule that tells Bindbee where to read the value from in the raw upstream payload. A mapping is either: * **Organization-scoped** (`integration_slug` set) — applies to every connector of that integration in your org. * **Connector-scoped** (`connector_token` set) — applies to a single connector instance and overrides the org-level mapping. * **JMESPath** — the expression language used to point at a value inside the raw upstream JSON. See [jmespath.org](https://jmespath.org/) for the full reference. ## Typical workflow A complete end-to-end flow when defining a new custom field via the API looks like this: Use the lookup endpoints to fetch valid `category`, `model`, and `integration_slug` values that are available to use. Fetch a sample of the raw third-party response so you can see exactly what is available to JMESPath against. Dry-run your expression against real connector data — or an inline payload — without persisting anything. Recommended to avoid `INVALID_JSON_PATH` in production responses. Register the field on the target `(category, model)`. Attach the field to either an integration (org-scoped) or a single connector (connector-scoped). Inspect the effective per-connector view to confirm which mapping is winning and which fields are still unmapped. The rest of this page walks through the same flow with example requests and responses, using a running example: extending the `employee` model with a `guardian_mobile` field, populated from a Workday connector. ## Step 1: Discover models and integrations Use the lookup endpoints to fetch the slugs you'll need. These are filtered to what your organization actually has access to. ```bash theme={null} # Models you can extend GET /api/v1/lookup/models?category=HRIS # Integrations enabled for your org GET /api/v1/lookup/integrations?category=HRIS ``` ```json Example responses theme={null} // /api/v1/lookup/models { "models": [ { "slug": "employee", "display_name": "Employee", "category": "HRIS" } ] } // /api/v1/lookup/integrations { "integrations": [ { "slug": "workday", "display_name": "Workday", "categories": ["HRIS"] } ] } ``` Connector tokens (used for connector-scoped mappings) are **not** returned by any lookup endpoint. They are issued when a connector is created and surfaced through the connector flow. ## Step 2: Inspect the raw upstream payload Before you write a JMESPath, look at the shape of the upstream JSON. The `raw-data` endpoint returns either the connector's latest synced row or a canned sample for the integration if no connector is provided (or if it has not synced yet). ```bash theme={null} # Using a real connector GET /api/v1/custom-fields/raw-data?category=HRIS&model=employee&connector_token= # Or just the integration sample GET /api/v1/custom-fields/raw-data?category=HRIS&model=employee&integration_slug=workday ``` ```json Example response theme={null} { "data": { "employee": { "id": "emp_123", "first_name": "Ada", "guardian_mobile": "+1-555-123-4567" } } } ``` In this example, `data.employee.guardian_mobile` is the JMESPath you'll want to use. ## Step 3: Validate the JMESPath (optional but recommended) The preview endpoint evaluates a JMESPath against real connector data **without** persisting anything. Use this to catch typos and confirm the resolved value type before you save the mapping. ```bash theme={null} POST /api/v1/custom-fields/preview ``` ```json Request body theme={null} { "connector_token": "", "category": "HRIS", "model": "employee", "json_path": "data.employee.guardian_mobile" } ``` ```json Response theme={null} { "connector_token": "", "json_path": "data.employee.guardian_mobile", "resolved_value": "+1-555-123-4567", "resolved_value_type": "string", "raw_data_source": "connector_sync" } ``` `raw_data_source` will be one of: * `"connector_sync"` — evaluated against the connector's latest synced row. * `"integration_sample"` — connector hasn't synced yet, fell back to the canned sample. * `"inline"` — the request supplied an inline `raw_data` payload to evaluate against. `resolved_value_type` will be one of: `string`, `number`, `boolean`, `object`, `array`, `null`. ## Step 4: Create the custom field Once you're confident in your JMESPath, register the field itself. The field is just a typed slot on the `(category, model)` — it carries no mapping yet. ```bash theme={null} POST /api/v1/custom-fields ``` ```json Request body theme={null} { "name": "guardian_mobile", "description": "Employee's guardian mobile number", "category": "HRIS", "model": "employee" } ``` ```json Response theme={null} { "id": "018e586b-7d0b-7bc9-be65-a8fdbc82d734", "status": "SUCCESS" } ``` `name` must be **snake\_case**, between 2 and 128 characters, and unique within the `(category, model)`. Fields created here will appear with `source: "API"` when listed. ## Step 5: Create a mapping A mapping pairs the custom field with a JMESPath, scoped either to an integration (applies to every connector of that integration) or to a single connector (overrides the org-level mapping for that connector only). Applies to every Workday connector in your organization. ```bash theme={null} POST /api/v1/custom-fields/mapping ``` ```json Request body theme={null} { "custom_field_id": "018e586b-7d0b-7bc9-be65-a8fdbc82d734", "integration_slug": "workday", "json_path": "data.employee.guardian_mobile" } ``` Applies to one specific connector and overrides any org-level mapping for the same field on that connector. ```bash theme={null} POST /api/v1/custom-fields/mapping ``` ```json Request body theme={null} { "custom_field_id": "018e586b-7d0b-7bc9-be65-a8fdbc82d734", "connector_token": "", "json_path": "data.employee.guardian_mobile" } ``` ```json Response theme={null} { "id": "018e586b-7d0b-7bc9-be65-a8fdbc82d734", "status": "SUCCESS" } ``` Provide **exactly one** of `integration_slug` or `connector_token`. To change the scope of an existing mapping, delete it and create a new one — only `json_path` is mutable via update. ## Step 6: Verify the effective configuration The configuration endpoint returns every custom field for a given `(connector, category, model)` together with the **effective** mapping in use. Connector-scoped mappings override organization-scoped ones, and unmapped fields are included with `json_path: null` so you can see what's still left to configure. ```bash theme={null} GET /api/v1/custom-fields/configuration?connector_token=&category=HRIS&model=employee ``` ```json Response theme={null} { "connector_token": "", "integration_slug": "workday", "category": "HRIS", "model": "employee", "fields": [ { "custom_field_id": "018e586b-7d0b-7bc9-be65-a8fdbc82d734", "name": "guardian_mobile", "description": "Employee's guardian mobile number", "category": "HRIS", "model": "employee", "json_path": "data.employee.guardian_mobile", "source": "organization", "mapping_id": "018e586b-7d0b-7bc9-be65-a8fdbc82d734" }, { "custom_field_id": "...", "name": "favorite_color", "description": null, "category": "HRIS", "model": "employee", "json_path": null, "source": null, "mapping_id": null } ] } ``` Each entry's `source` indicates which mapping is winning: * `"connector"` — a connector-scoped mapping is in effect. * `"organization"` — no connector override; the org-scoped mapping is being inherited. * `null` — no mapping is configured for this field on this connector. ## Updating and deleting | Action | Endpoint | Notes | | --------------------- | ------------------------------------------- | --------------------------------------------------------- | | List custom fields | `GET /api/v1/custom-fields` | Supports `category`, `model`, and `source` filters. | | Get one with mappings | `GET /api/v1/custom-fields/{id}` | Returns the field plus all its mappings. | | Delete a field | `DELETE /api/v1/custom-fields/{id}` | Cascades — all mappings for the field are removed. | | Update a mapping | `PATCH /api/v1/custom-fields/mapping/{id}` | Only `json_path` is mutable. Re-create to change scope. | | Delete a mapping | `DELETE /api/v1/custom-fields/mapping/{id}` | Removes a single mapping; the field itself is unaffected. | ## Next steps * Learn how to [retrieve Custom Fields in API responses](/custom-fields/overview#using-custom-fields-in-responses). * Review the [Best Practices](/custom-fields/overview#best-practices) for naming and scoping. # Create Custom Field Source: https://docs.bindbee.dev/custom-fields/api/create-custom-field post /api/v1/custom-fields Register a new custom field on a `(category, model)` pair. The field carries no mapping yet — attach one with the create mapping endpoint. # Create Mapping Source: https://docs.bindbee.dev/custom-fields/api/create-mapping post /api/v1/custom-fields/mapping Create a custom field mapping. Provide exactly one of `integration_slug` (organization scope) or `connector_token` (connector scope). # Delete Custom Field Source: https://docs.bindbee.dev/custom-fields/api/delete-custom-field delete /api/v1/custom-fields/{custom_field_id} Delete a custom field. Cascades — all mappings attached to the field are removed as well. # Delete Mapping Source: https://docs.bindbee.dev/custom-fields/api/delete-mapping delete /api/v1/custom-fields/mapping/{custom_field_mapping_id} Delete a custom field mapping. Removes a single mapping; the custom field itself is unaffected. # Get Configuration Source: https://docs.bindbee.dev/custom-fields/api/get-configuration get /api/v1/custom-fields/configuration Return the full custom-field configuration for a connector + (category, model): every field with its effective mapping (connector overrides organization). Unmapped fields are included with `json_path: null` so callers can see what is left to configure. # Get Custom Field Source: https://docs.bindbee.dev/custom-fields/api/get-custom-field get /api/v1/custom-fields/{custom_field_id} Get a custom field by ID, including all of its mappings (organization- and connector-scoped). # Get Raw Data Source: https://docs.bindbee.dev/custom-fields/api/get-raw-data get /api/v1/custom-fields/raw-data Inspect the raw upstream payload for a connector + (category, model). Use this to discover the field structure before writing a JMESPath. Provide exactly one of `connector_token` or `integration_slug`. # List Custom Fields Source: https://docs.bindbee.dev/custom-fields/api/list-custom-fields get /api/v1/custom-fields List custom fields for the caller's organization. Supports filtering by `category`, `model`, and `source` (`API` vs `DASHBOARD`). # List Mappings Source: https://docs.bindbee.dev/custom-fields/api/list-mappings get /api/v1/custom-fields/mapping List custom field mappings for an integration (org-scoped) or a connector (connector-scoped). Provide exactly one of `integration_slug` or `connector_token`. # Preview Source: https://docs.bindbee.dev/custom-fields/api/preview post /api/v1/custom-fields/preview Dry-run a JMESPath against a connector's data without persisting it as a mapping. Use this to validate an expression before saving. # Update Mapping Source: https://docs.bindbee.dev/custom-fields/api/update-mapping patch /api/v1/custom-fields/mapping/{custom_field_mapping_id} Update a custom field mapping. Only `json_path` is mutable — to change the scope of a mapping, delete it and create a new one. # Dashboard Configuration Source: https://docs.bindbee.dev/custom-fields/dashboard Create and configure Custom Fields visually from the Bindbee Dashboard. This page walks through creating a Custom Field from the Bindbee Dashboard. For the concepts (what Custom Fields are, organization vs connector scope) see the [Custom Fields overview](/custom-fields/overview). For the programmatic equivalent, see [API Configuration](/custom-fields/api-workflow). Creating a Custom Field from the dashboard is a two-step process. **Step 1:** Create New Field **Step 2:** Configure Field Mappings ## Step 1: Create New Field * Click the "New Field" button in the top right corner * Fill out the necessary details in the dialog. * Click "Create" Name of custom field must be **unique** and naming should follow **snake\_case** ## Step 2: Configure Field Mappings After creating the field, you'll be redirected to the mapping configuration screen where you can set up both `Connector Level` and `Organization Level` mappings. ### Connector Level Mapping * Click on "Connector Level" * Select your connector from the Dropdown * Fill or Select Data field box, you can choose either * Select Mapping from Options * Click the `` button to view the raw JSON response and click the desired field * Enter the JSON Path(JMESPath) expression directly #### Find Fields Using Employee Search The field you want to map may not be available for every employee in the connector. In some cases, it may only appear in the `raw_data` returned for a specific employee. You can use Employee Search to find an employee and review their connector data while configuring the mapping. * Click the employee search field at the top of the data panel. * Enter the employee's name or email address. * Select the appropriate employee from the search results. * Review the fields in the employee's raw data displayed in the panel on the right. * Find and select the field you want to map. * Click "Map Field" to confirm the custom field mapping. Employee Search displays live employee data returned by the selected connector, making it easier to locate fields that are only available for certain employees. Data structures may differ across integrations depending on how each platform structures and returns its information. If your JSON Path (JMESPath) expression is incorrect, then while requesting the `custom_fields` via APIs, you will not receive an error. Instead, the API will respond with `"INVALID_JSON_PATH"` mapped to the incorrectly configured field. Ensure that all JMESPath expressions are valid to retrieve the intended data correctly. ### Organization Level Mapping * Click on "Organization Level" * Select your integration from the Dropdown * Fill or Select Data field box, you can choose either * Select Mapping from Options * Click the `` button to view the raw JSON response and click the desired field * Enter the JSON Path(JMESPath) expression directly. You can add multiple mappings to a single custom field by clicking the "Add New" button and repeating the process for different connectors or integrations. ## Next steps * Learn how to [retrieve Custom Fields in API responses](/custom-fields/overview#using-custom-fields-in-responses). * Review the [Best Practices](/custom-fields/overview#best-practices) for naming and scoping. # List Integrations Source: https://docs.bindbee.dev/custom-fields/lookup/list-integrations get /api/v1/lookup/integrations List integrations enabled for the caller's organization. Use the returned `slug` as the `integration_slug` when creating organization-scoped custom field mappings. # List Models Source: https://docs.bindbee.dev/custom-fields/lookup/list-models get /api/v1/lookup/models List unified models enabled for the caller's organization. Use the returned `slug` as the `model` field when creating custom fields or mappings. # Custom Fields Source: https://docs.bindbee.dev/custom-fields/overview Custom Fields let you extend Bindbee's unified models with additional fields specific to your use case. They make it possible to surface any value from a third-party API response — alongside the standard unified schema — by mapping it through a JMESPath expression. ## Overview A unified model has a fixed schema (e.g. an `employee` always exposes `first_name`, `email`, etc.). Custom Fields let you extend that schema with extra attributes — for example `guardian_mobile` — and tell Bindbee how to populate them by pointing a [JMESPath](https://jmespath.org/) expression at the raw upstream payload. ## Scopes Every Custom Field mapping is attached to one of two scopes: * **Connector Level**: Applied to a specific connector instance. * **Organization Level**: Applied to all connectors of a specific integration type. When a mapping exists at both levels for the same field, the **connector-level** mapping takes precedence and overrides the organization-level one for that connector. ## Choosing how to manage Custom Fields You can manage Custom Fields in two ways. Both create the same underlying configuration and can coexist — pick whichever fits the task. Visual flow inside the Bindbee Dashboard. Best for ad-hoc setup, exploring upstream payloads interactively, and one-off field changes. Programmatic flow for automation, infrastructure-as-code, replicating configuration across environments, or syncing with internal admin tools. Fields created from the API are tagged with `source: "API"`. Fields created from the dashboard are tagged with `source: "DASHBOARD"`. Both behave identically once created and appear together in lookups and responses. ## Using Custom Fields in Responses Once a field has at least one mapping that resolves on a connector, you can request it back in the unified API response by adding `include_custom_fields=true` to your request. For example, to fetch employees with custom fields populated: ```http theme={null} GET https://api.bindbee.dev/api/hris/v1/employees?include_custom_fields=true ``` Example response with custom fields: ```json theme={null} { "id": "emp_123", "name": "John Doe", "custom_fields": { "guardian_mobile": "+1-555-123-4567", "cost_center_name": "Engineering-NA", "hourly_rate": 45.0 } } ``` If a JMESPath expression is invalid or fails to resolve, the response will contain `"INVALID_JSON_PATH"` in place of the value for that field rather than raising an error. Validate your expression before saving — via the dashboard's preview button or the API's [`/custom-fields/preview`](/custom-fields/api-workflow#step-3-validate-the-jmespath-optional-but-recommended) endpoint. ## Best Practices 1. **Naming Conventions** * Use clear, descriptive names. * Follow snake\_case formatting. * Avoid generic names like `custom1`, `custom2`. 2. **JMESPath Expressions** * Always preview an expression before saving it. * Consider data type consistency across connectors of the same integration. 3. **Organization vs Connector Level Mapping** * Use **Organization Level** mappings for data that's consistent across all connectors of an integration. * Use **Connector Level** mappings for connector-specific overrides. * Review existing **Organization Level** mappings before creating connector-level duplicates. 4. **Automation** * Use the [API Configuration](/custom-fields/api-workflow) flow to keep custom field configuration in version control or replicate it across environments (dev → staging → prod). * The `configuration` endpoint is a good health check — run it after provisioning a new connector to confirm every expected field has a mapping. For more details on writing JMESPath expressions, see the [JMESPath Documentation](https://jmespath.org/). # Connectors Source: https://docs.bindbee.dev/features/connectors View Connectors A connector is like a bridge that connects your application with the third-party tool you want to integrate with. Each connector is specific to the particular integration and helps to transfer data smoothly between the two platforms. Connector You can also **Track Issues** and see **Logs** In other words, it's a unique link between your product and the tool your customer is using. # Dashboard Source: https://docs.bindbee.dev/features/dashboard Our easy-to-use Dashboard provides a central location for managing all of your Bindbee integrations. With the Dashboard, you'll be able to get a bird's eye view of how your integrations are performing, How many API requests have been made, which of your customer is using which connector or are there issues pertaining with some connectors and make any necessary adjustments on the fly. You can check the number of API calls made, and the status of the connectors. Moreover, it helps you get an overview of the logs and prevailing issues with any connectors. Dashboard # Development Source: https://docs.bindbee.dev/features/environments/development Bindbee Development environment for building and testing integrations. ## Overview The Development environment is a sandbox designed for building and testing your Bindbee integrations. It is intended strictly for non-production use and is completely isolated from the Production environment. The Development environment allows unrestricted testing without impacting live production data. ## Development API Key Your Development API key (`dev_api_key`) is used exclusively to access the Development environment. It cannot be used to access the Production environment. Development API keys should only be used for testing and should never be used in production applications. ### Sharing Development Keys Development API keys are intended for testing purposes only and can be safely shared within your team for development and testing workflows. ## Authentication When making requests to the Bindbee API in the Development environment, include your Development API key in the `Authorization` header using the **Bearer** prefix: `Authorization: Bearer YOUR_DEV_API_KEY` ## Connector Behavior ### Sync Configuration Connectors created using Development API keys do not resync automatically. You can trigger syncs either programmatically or manually via the dashboard. Unlike Production connectors, syncs are not automated for Development connectors. You must trigger syncs manually or programmatically to fetch updated data. ### Connector Limits There is no limit on the number of connectors you can create in the Development environment, allowing you to test multiple integrations without restrictions. ## Managing Your Development API Key You can view and regenerate your Development API key from the **API Keys** section of your Bindbee dashboard: [https://app.bindbee.dev/settings/keys](https://app.bindbee.dev/settings/keys) Development API Keys ## Environment Isolation The Development and Production environments are completely isolated: * Development API keys only access Development data * Production API keys only access Production data * Connectors and data do not transfer between environments # Production Source: https://docs.bindbee.dev/features/environments/production Bindbee Production environment for live customer integrations. ## Overview The Production environment is used for running live customer integrations. It handles real customer data and is intended for use with customers actively operating in production. This environment is completely isolated from the Development environment. Production API keys provide access to real customer data. Keep them secure and never expose them publicly. ## Production API Key Your Production API key (`prod_api_key`) is used exclusively to access the Production environment. It cannot be used to access the Development environment. Only Production API keys can be used to access the Production environment. Development API keys are strictly limited to the Development environment. ### Managing Your Production API Key You can view and regenerate your Production API key in the [API Keys](https://app.bindbee.dev/settings/keys) section of your Bindbee dashboard. Production API Keys Regenerating your Production API key will immediately invalidate the previous key. Ensure all dependent services are updated before regenerating. ### Authentication Include your Production API key in the `Authorization` header for all requests: `Authorization: Bearer YOUR_PROD_API_KEY` ## Sync Behavior In the Production environment, data syncs are handled automatically. Data synchronization occurs once every 24 hours by default. This sync frequency can be adjusted based on customization preferences. ## Intended Usage The Production environment is designed for: * Live customer integrations * Real-time and scheduled data synchronization * Stable, production-ready workflows It should only be used once integrations have been fully tested in the Development environment. ## Environment Isolation The Production and Development environments are completely isolated: * Production API keys only access Production data * Development API keys only access Development data * Connectors and data do not transfer between environments # Logs Source: https://docs.bindbee.dev/features/logs View Logs Bindbee Logs gives you real-time visibility into your API request and response data. With Logs, you'll be able to quickly identify the Request and response for a particular connector. It's a hub where you get to see the direction of data, if it's READ or WRITE. Also with every request you get to understand the response. If the response is bad, system automatically shows the error code. This is the first place you may want to go incase you wish to see if integration is working or not. Logs You can also see detailed view of perticular request Request Log # Magic Link Source: https://docs.bindbee.dev/features/magic-link Enable seamless integration for your end users without the need for any code. Bindbee's Magic Link feature offers B2B tech companies an easy way to deliver an in-browser Link experience to their end-users without requiring any front-end code. With this feature, you can deliver a secure URL to your end-users that will provide them with an intuitive, user-friendly experience. Create Magic Link Select Tool Fill in the details of your customer (end-user) and share the link with them. Link Dialog Your customer opens this link and authorizes your company to get the data from their tool. Magic Link Connection Connection Established Once the end customer sees this screen, the connector will be now live in your account in Bindbee. # Overview Source: https://docs.bindbee.dev/features/overview Bindbee ensure that B2B technology companies can do HRIS, Payroll, ATS and LMS integrations without writing much code. We have built Unified APIs that ensure that our customers never have to build 1-on-1 integrations. And, all the third party HRIS, Payroll, ATS and LMS integrations can be done in less than 5 minutes. This is a detailed documentation to help you understand the **"how"** ## Bindbee Features Here you'll find everything you need to know about Bindbee's key features. As a Unified API for third-party customer facing B2B integrations, Bindbee makes it easy for B2B technology companies to integrate with multiple HRIS, Payroll, ATS and LMS software at once. With over 100+ integrations already in place, Bindbee offers unparalleled scalability and convenience. To help you get started, and get acquainted with Bindbee, we have elaborated on all the product features: Each of these features is designed to make integration and management as seamless as possible. Click on the links below to learn more about each feature and how it can help your business grow. Our easy-to-use Dashboard provides a central location for managing all of your Bindbee integrations. Bindbee's Magic Link feature offers B2B tech companies an easy way to deliver an in-browser Link experience to their end-users without requiring any front-end code. A connector is like a bridge that connects your application with the third-party tool you want to integrate with. Bindbee Logs gives you real-time visibility into your API request and response data. Bindbee Issues is an automated error tracking and resolution feature. # Get Bank Info By Id Source: https://docs.bindbee.dev/hris/bank-info/get-bank-info-by-id get /api/hris/v1/bank-info/{id} Returns a BankInfo object with the given id. # Get Bank Info List Source: https://docs.bindbee.dev/hris/bank-info/get-bank-info-list get /api/hris/v1/bank-info Returns a list of BankInfo objects. # Get Benefit Coverage By Id Source: https://docs.bindbee.dev/hris/benefit-coverages/get-benefit-coverage-by-id get /api/hris/v1/benefit-coverages/{id} Returns a Benefit Coverage object with the given id. # Get Benefit Coverages Source: https://docs.bindbee.dev/hris/benefit-coverages/get-benefit-coverages get /api/hris/v1/benefit-coverages Returns a list of Benefit Coverage objects. # Get Benefit By Id Source: https://docs.bindbee.dev/hris/benefits/get-benefit-by-id get /api/hris/v1/benefits/{id} Returns a Benefit object with the given id. # Get Benefits Source: https://docs.bindbee.dev/hris/benefits/get-benefits get /api/hris/v1/benefits Returns a list of Benefit objects. # Get Companies Source: https://docs.bindbee.dev/hris/companies/get-companies get /api/hris/v1/companies Returns a list of Companies objects. # Get Company By Id Source: https://docs.bindbee.dev/hris/companies/get-company-by-id get /api/hris/v1/companies/{id} Returns a Company object with the given id. # Get Compensation By Id Source: https://docs.bindbee.dev/hris/compensation/get-compensation-by-id get /api/hris/v1/compensations/{id} Returns a Compensation object with the given id. # Get Compensations Source: https://docs.bindbee.dev/hris/compensation/get-compensations get /api/hris/v1/compensations Returns a list of Compensation objects. # Get Dependent Benefit By Id Source: https://docs.bindbee.dev/hris/dependent-benefits/get-dependent-benefit-by-id get /api/hris/v1/dependent-benefits/{id} Returns a Dependent Benefit object with the given id. # Get Dependent Benefits Source: https://docs.bindbee.dev/hris/dependent-benefits/get-dependent-benefits get /api/hris/v1/dependent-benefits Returns a list of Dependent Benefit objects. # Get Dependent By Id Source: https://docs.bindbee.dev/hris/dependents/get-dependent-by-id get /api/hris/v1/dependents/{id} Returns a Dependent object with the given id. # Get Dependents Source: https://docs.bindbee.dev/hris/dependents/get-dependents get /api/hris/v1/dependents Returns a list of Dependent objects. # Get Document By Id Source: https://docs.bindbee.dev/hris/documents/get-document-by-id get /api/hris/v1/documents/{id} **Beta.** The Documents API is in beta and its contract may change. Returns a Document object with the given id, including the parsed `data` object when `data_status` is `AVAILABLE`. This is the only endpoint that returns `data`; it is otherwise identical to the list response. # Get Document Download URL Source: https://docs.bindbee.dev/hris/documents/get-document-download-url get /api/hris/v1/documents/{id}/download **Beta.** The Documents API is in beta and its contract may change. Returns an expiring, signed URL from which the document file can be downloaded, along with its filename, MIME type and size. This is a separate call from Get Document By Id because the URL expires within minutes while the document metadata stays cacheable for hours, and because downloading a signed W-4 warrants its own permission and its own audit event. # Get Documents Source: https://docs.bindbee.dev/hris/documents/get-documents get /api/hris/v1/documents **Beta.** The Documents API is in beta and its contract may change. Returns a list of Document objects. The response carries document metadata and the parse state of each document, but never the file itself and never the parsed values: read `data_status` to decide which documents are worth fetching in full, then call Get Document By Id for those. # Create Employee Payroll Run Source: https://docs.bindbee.dev/hris/employee-payroll-runs/create-employee-payroll-runs POST /api/hris/v1/employee-payroll-runs Creates a Employee Payroll Run object with the given values. This endpoint creates an Employee Payroll Run in the connected HRIS via Bindbee's unified **Employee Payroll Runs** model. ## Provider-specific behavior Different HRIS systems can have slightly different requirements and behaviors when creating payroll runs. Use the accordions below to see details for each provider.

When the underlying HRIS is Workday, an `earning` can be created using a payload like:

```json theme={null} { "employee": "019b2647-8e14-7fcf-aa9e-aeff6ab205b2", "start_date": "2025-12-01", "end_date": "2025-12-31", "earnings": [ { "type": "Workday_Earning_Code", "amount": 200, "id": "W_1042S_2015" } ] } ```

Similarly, a `deduction` can be created using a payload like:

```json theme={null} { "employee": "019b2647-8e14-7fcf-aa9e-aeff6ab205b2", "start_date": "2025-12-01", "end_date": "2025-12-31", "deductions": [ { "type": "Workday_Deduction_Code", "amount": 200, "id": "W_1042S_2015" } ] } ``` ### Important note * For Workday, you can add either `earnings` or `deductions` at a time - you cannot add both simultaneously. * You can only add 1 `earning` or `deduction` at a time, no more than that. * `payroll_run` field must be set to **null** for Workday integration. The possible types for `earnings` are: * **Workday\_Earning\_Code** * **WID** The possible types for `deductions` are: * **Workday\_Deduction\_Code** * **WID**

When the underlying HRIS is ADP , an `employee_payroll_run` can be created using a payload like:

```json theme={null} { "employee": "01929ee7-28b6-7abc-b4d3-e78610669c82", "earnings": [ { "code": "BN", "amount": 0.01 }, { "code": "R", "amount": 0.04 } ], "deductions": [ { "code": "REI", "amount": 0.01 }, { "code": "KL1", "amount": 0.04 } ], "taxes": [ { "code": "9" } ], "payroll_file": "023321", "payroll_process_name": "Ded Ear writeback" } ```

When the underlying HRIS is UKG Ready , an `employee_payroll_run` can be created using a payload like:

```json theme={null} { "employee": "0194cbef-a29f-7afe-8fb6-bcb706383346", "payroll_run": "0194cbef-a8e7-7784-ae98-bb6adaac63c6", "paystatement_type_id": 51743758, "deductions": [ { "company_deduction": 5.5, "employee_deduction": 10.2, "name": "Student Loan" }, { "company_deduction": 1.5, "name": "Dental PreTax" } ], "earnings": [ { "name": "Overtime", "amount": 12.9 } ] } ```

When the underlying HRIS is **SAP SuccessFactors**, an `employee_payroll_run` can be created using a payload like:

```json theme={null} { "employee": "019bdafb-0afb-782b-b03b-01c23271653d", "payroll_provider_id": "BindBee", "payroll_person_id": "310", "payroll_employment_id": "270", "start_date": "2026-01-26T08:00:00Z", "end_date": "2026-01-31T08:00:00Z", "deductions": [ { "employee_deduction": 200, "name": "401K" } ], "earnings": [ { "name": "GROSS", "amount": 2000 } ], "taxes": [ { "name": "Testing_Tax", "amount": 101 } ] } ``` ### Where to find these fields: * **payroll\_provider\_id**: Use the ID provided by the customer if it’s available. If not, any valid string can be used. * **payroll\_person\_id**: This value can be found in the employee’s `raw_data`. * **payroll\_employment\_id**: Source this either from the employee’s `raw_data` or from Binbee’s employment model (`remote_id`). * **Deductions**: When adding deductions, ensure the deduction codes are included in the name field. ### Important notes * All top-level fields are mandatory. * `earnings`, `deductions`, and `taxes` can be sent together. * All fields inside each array are compulsory.
# Get Create Employee Payroll Run Meta Source: https://docs.bindbee.dev/hris/employee-payroll-runs/get-create-employee-payroll-run-meta get /api/hris/v1/employee-payroll-runs/meta/post Returns request schema for POST Employee Payroll Run request. # Get Employee Payroll Runs Source: https://docs.bindbee.dev/hris/employee-payroll-runs/get-employee-payroll-runs get /api/hris/v1/employee-payroll-runs Returns a list of EmployeePayrollRun objects. # Get Employee Payroll Runs By Id Source: https://docs.bindbee.dev/hris/employee-payroll-runs/get-employee-payroll-runs-by-id get /api/hris/v1/employee-payroll-runs/{id} Returns an EmployeePayrollRun object with the given id # Create Employee Source: https://docs.bindbee.dev/hris/employee/create-employee post /api/hris/v1/employees Creates an Employee object with the given values. To create an employee in HRIS, you first need to fetch the request body structure that HRIS expects. This structure varies based on the HRIS provider (e.g., BambooHR, ADP, Workday). The request body consists of three parts: * **Model Keys**: Standard attributes required for all employees. * **Additional Attributes**: Specific fields required by the chosen HRIS. * **Custom Fields**: Extra metadata for specific implementations. **Note:** Some attributes may have `allowed_values`, meaning only specific values are permitted based on the customer's HRIS configuration. Ensure you check for these constraints before submitting the request. ## Step 1: Fetch the Required Request Body Before calling the **Create Employee** API, you need to get the request structure using the [Meta Create Employee](/hris/employee/get-create-employee-meta) API. ## Step 2: Create an Employee Once you have the request body structure, use the **Create Employee** API to send the request. # Get Create Employee Meta Source: https://docs.bindbee.dev/hris/employee/get-create-employee-meta get /api/hris/v1/employees/meta/post Returns request schema for POST Employee request. # Get Create Employee Request Body Source: https://docs.bindbee.dev/hris/employee/get-create-employee-request-body get /api/hris/v1/employees/create/meta Returns the data points required to add new employee in HRIS # Get Employee By Id Source: https://docs.bindbee.dev/hris/employee/get-employee-by-id get /api/hris/v1/employees/{id} Returns an Employee object with the given id. # Get Employees Source: https://docs.bindbee.dev/hris/employee/get-employees get /api/hris/v1/employees Returns a list of Employee objects. # Get Employer Benefit By Id Source: https://docs.bindbee.dev/hris/employer-benefits/get-employer-benefit-by-id get /api/hris/v1/employer-benefits/{id} Returns a Employer Benefit object with the given id. # Get Employer Benefits Source: https://docs.bindbee.dev/hris/employer-benefits/get-employer-benefits get /api/hris/v1/employer-benefits Returns a list of Employer Benefit objects. # Get Employment By Id Source: https://docs.bindbee.dev/hris/employments/get-employment-by-id get /api/hris/v1/employments/{id} Returns an Employment object with the given id. # Get Employments Source: https://docs.bindbee.dev/hris/employments/get-employments get /api/hris/v1/employments Returns a list of Employment objects. # Get Group By Id Source: https://docs.bindbee.dev/hris/groups/get-group-by-id get /api/hris/v1/groups/{id} Returns a Group object with the given id. # Get Groups Source: https://docs.bindbee.dev/hris/groups/get-groups get /api/hris/v1/groups Returns a list of Group objects. # Get Location By Id Source: https://docs.bindbee.dev/hris/locations/get-location-by-id get /api/hris/v1/locations/{id} Returns a Location object with the given id. # Get Locations Source: https://docs.bindbee.dev/hris/locations/get-locations get /api/hris/v1/locations Returns a list of Location objects. # Get Pay Group By Id Source: https://docs.bindbee.dev/hris/pay-groups/get-pay-group-by-id get /api/hris/v1/pay-groups/{id} Returns a PayGroup object with the given id. # Get Pay Groups Source: https://docs.bindbee.dev/hris/pay-groups/get-pay-groups get /api/hris/v1/pay-groups Returns a list of PayGroup objects. # Get Payroll Code By Id Source: https://docs.bindbee.dev/hris/payroll-codes/get-payroll-code-by-id get /api/hris/v1/payroll-codes/{id} Returns a Payroll Code object with the given id. # Get Payroll Codes Source: https://docs.bindbee.dev/hris/payroll-codes/get-payroll-codes get /api/hris/v1/payroll-codes Returns a list of Payroll Code objects. # Get Payroll Calendar Run By Id Source: https://docs.bindbee.dev/hris/payroll-run-calendar/get-payroll-run-calendar-by-id get /api/hris/v1/payroll-run-calendars/{id} Returns a PayrollRunCalendar object with the given id. # Get Payroll Run Calendars Source: https://docs.bindbee.dev/hris/payroll-run-calendar/get-payroll-run-calendars get /api/hris/v1/payroll-run-calendars Returns a list of PayrollRunCalendar objects. # Get Payroll Run By Id Source: https://docs.bindbee.dev/hris/payroll-runs/get-payroll-run-by-id get /api/hris/v1/payroll-runs/{id} Returns a PayrollRun object with the given id. # Get Payroll Runs Source: https://docs.bindbee.dev/hris/payroll-runs/get-payroll-runs get /api/hris/v1/payroll-runs Returns a list of PayrollRun objects. # Get Time Off Balance By Id Source: https://docs.bindbee.dev/hris/time-off-balance/get-time-off-balance-by-id get /api/hris/v1/time-off-balances/{id} Returns a TimeOffBalance object with the given id. # Get Time Off Balances List Source: https://docs.bindbee.dev/hris/time-off-balance/get-time-off-balances-list get /api/hris/v1/time-off-balances Returns a list of TimeOffBalance objects. # create-time-off Source: https://docs.bindbee.dev/hris/time-off/create-time-off post /api/hris/v1/time-off Creates a TimeOff object with the given values. This endpoint creates a Timeoff Entry in the connected HRIS via Bindbee's unified **Time Off** model. ## Provider-specific behavior Different HRIS systems can have slightly different requirements and behaviors when creating timeoff entries. Use the accordions below to see details for each provider.

When the underlying HRIS is ADP, a Timesheet Entry can be created using a payload like:

```json theme={null} { "employee": "01929f6e-c6c1-74b4-8119-a7d9efc0c8b8", "amount": 8, "start_time": "2024-09-04T08:00:00Z", "end_time": "2024-09-04T16:00:00Z", "request_type": "VACATION" } ```

Use the Get Create Time Off Meta endpoint to retrieve the required schema and values.

Use the Get Create Time Off Meta endpoint to retrieve the required schema and values.

# Get Create Time Off Meta Source: https://docs.bindbee.dev/hris/time-off/get-create-time-off-meta get /api/hris/v1/time-off/meta/post Returns request schema for POST Time off request. # Get Time Off By Id Source: https://docs.bindbee.dev/hris/time-off/get-time-off-by-id get /api/hris/v1/time-off/{id} Returns a TimeOff object with the given id. # Get Time Off List Source: https://docs.bindbee.dev/hris/time-off/get-time-off-list get /api/hris/v1/time-off Returns a list of TimeOff objects. # create-timesheet-entries Source: https://docs.bindbee.dev/hris/timesheet-entries/create-timesheet-entries post /api/hris/v1/timesheet-entry Creates a Time sheet object with the given values. This endpoint creates a Timesheet Entry in the connected HRIS via Bindbee's unified **Time Sheet Entry** model. ## Provider-specific behavior Different HRIS systems can have slightly different requirements and behaviors when creating timesheet entries. Use the accordions below to see details for each provider.

When the underlying HRIS is Bamboo HR, a Timesheet Entry can be created using a payload like:

```json theme={null} { "employee": "019ab506-66a2-7f6e-b8da-09d3579a534e", "date": "2025-11-24", "start_time": "2025-11-24T08:00:00Z", "end_time": "2025-11-24T17:00:00Z" } ```

When the underlying HRIS is Personio, a Timesheet Entry can be created using a payload like:

```json theme={null} { "employee": "0198e59a-876f-748c-8e38-c56e6246f8aa", "break": 8, "date": "2025-08-04T08:00:00Z", "start_time": "2025-08-04T08:00:00Z", "end_time": "2025-08-04T16:00:00Z" } ```

When the underlying HRIS is Xero, a Timesheet Entry can be created using a payload like:

```json theme={null} { "employee": "0198e59a-876f-748c-8e38-c56e6246f8aa", "start_time": "2025-08-04T08:00:00Z", "end_time": "2025-08-04T16:00:00Z" } ```

When the underlying HRIS is QuickBooks, a Timesheet Entry can be created using a payload like:

```json theme={null} { "employee": "0198e59a-876f-748c-8e38-c56e6246f8aa", "hours_worked": 8, "date": "2025-08-04T08:00:00Z", "start_time": "2025-08-04T08:00:00Z", "end_time": "2025-08-04T16:00:00Z" } ```

When the underlying HRIS is Workday, a Timesheet Entry can be created using a payload like:

```json theme={null} { "employee": "019d29f5-07cf-7396-9ae1-34e5b71ad42c", "date": "2026-03-26", "hours_worked": 7.5, "start_time": "2026-03-26T10:00:00Z", "end_time": "2026-03-26T17:30:00Z", "additional_attributes": { "worker_time_block_id": "TEST_BB", "time_entry_code_reference_wid": "c20c857a8cf41001aa4db119cb360000", "out_reason_reference_id": "Out" } } ```

Workday-specific requirements:

  • out\_reason\_reference\_id must be one of: Break, Meal, or Out.
  • For time\_entry\_code\_reference\_wid, search for All Time Entry Codes in the Workday portal and use the WID of any suitable code from the displayed list.
  • The selected Time Entry Code must have Units = Hours.
  • If the selected Time Entry Code has Entry Method = In/Out Only, out\_reason\_reference\_id is required.
  • If the selected Time Entry Code has Entry Method = Hours Only, out\_reason\_reference\_id must not be passed.

Use the Get Create Timesheet Meta endpoint to retrieve the required schema and values.

Contact Bindbee Support Team for additional details.

Contact Bindbee Support Team for additional details.

# Get Create Timesheet Meta Source: https://docs.bindbee.dev/hris/timesheet-entries/get-create-timesheet-meta get /api/hris/v1/timesheet-entry/meta/post Returns request schema for POST Timesheet entry request. # Get Timesheet By Id Source: https://docs.bindbee.dev/hris/timesheet-entries/get-timesheet-entries-by-id get /api/hris/v1/timesheet-entry/{id} Returns a Timesheet object with the given id. # Get Timesheet List Source: https://docs.bindbee.dev/hris/timesheet-entries/get-timesheet-entries-list get /api/hris/v1/timesheet-entry Returns a list of Timesheet entry objects. # Get Lms Categories Source: https://docs.bindbee.dev/lms/categories/get-categories get /api/lms/v1/categories Returns a list of LMS Category objects. # Get Lms Category By Id Source: https://docs.bindbee.dev/lms/categories/get-category-by-id get /api/lms/v1/categories/{id} Returns an LMS Category object with the given id. # Get Lms Completion By Id Source: https://docs.bindbee.dev/lms/completions/get-completion-by-id get /api/lms/v1/completions/{id} Returns an LMS Completion object with the given id. # Get Lms Completions Source: https://docs.bindbee.dev/lms/completions/get-completions get /api/lms/v1/completions Returns a list of LMS Completion objects. # Get Lms Content By Id Source: https://docs.bindbee.dev/lms/contents/get-content-by-id get /api/lms/v1/contents/{id} Returns an LMS Content object with the given id. # Get Lms Contents Source: https://docs.bindbee.dev/lms/contents/get-contents get /api/lms/v1/contents Returns a list of LMS Content objects. # Get Lms Course By Id Source: https://docs.bindbee.dev/lms/courses/get-course-by-id get /api/lms/v1/courses/{id} Returns an LMS Course object with the given id. # Get Lms Courses Source: https://docs.bindbee.dev/lms/courses/get-courses get /api/lms/v1/courses Returns a list of LMS Course objects. # Get Lms Enrollment By Id Source: https://docs.bindbee.dev/lms/enrollments/get-enrollment-by-id get /api/lms/v1/enrollments/{id} Returns an LMS Enrollment object with the given id. # Get Lms Enrollments Source: https://docs.bindbee.dev/lms/enrollments/get-enrollments get /api/lms/v1/enrollments Returns a list of LMS Enrollment objects. # Get Lms Skill By Id Source: https://docs.bindbee.dev/lms/skills/get-skill-by-id get /api/lms/v1/skills/{id} Returns an LMS Skill object with the given id. # Get Lms Skills Source: https://docs.bindbee.dev/lms/skills/get-skills get /api/lms/v1/skills Returns a list of LMS Skill objects. # Get Lms User By Id Source: https://docs.bindbee.dev/lms/users/get-user-by-id get /api/lms/v1/users/{id} Returns an LMS User object with the given id. # Get Lms Users Source: https://docs.bindbee.dev/lms/users/get-users get /api/lms/v1/users Returns a list of LMS User objects. # Meta APIs for Write Operations Source: https://docs.bindbee.dev/meta-apis-for-write-operations Retrieve request schemas for Create Employee, Create Employee Payroll Run, Create Timesheet, and Create Time Off Request APIs. ## Overview Meta APIs describe the request body expected by Bindbee for a specific write operation and integration. Each response follows a JSON Schema-compatible structure and includes field requirements, accepted values, formats, and nested object definitions. Because write capabilities vary by integration, the Meta API response should be used to construct and validate the request body before submitting a write operation. ## Supported Operations Meta APIs are currently available for the following write operations: * Create Employee * Create Employee Payroll Run * Create Timesheet * Create Time Off Request Support may vary by integration. A write operation can be available for an integration even when its Meta API schema has not yet been implemented. ## Request Workflow Use the following process when preparing a write request: 1. Call the Meta endpoint for the required write operation and integration. 2. Review the returned schema, including required fields, accepted values, and integration-specific parameters. 3. Construct a request body that conforms to the schema. 4. Submit the request to the corresponding write endpoint. The schema can also be used with compatible JSON Schema tooling for request validation or form generation. ## Meta API Response Structure A Meta API response defines the structure of the request body for the selected write operation. In addition to standard JSON Schema keywords, the response can include Bindbee-specific metadata such as `isRequired` and `enumInformation`. The following keywords are commonly included: | Keyword | Purpose | | ----------------- | ---------------------------------------------------- | | `type` | Defines the expected data type. | | `properties` | Defines fields within an object. | | `items` | Defines the structure of each element in an array. | | `required` | Lists the fields required within an object. | | `isRequired` | Indicates whether an individual field is required. | | `description` | Provides a human-readable explanation of a field. | | `format` | Defines an additional constraint for a string value. | | `enum` | Lists the accepted values for a field. | | `enumInformation` | Provides descriptions for values listed in `enum`. | ### Root Object and Field Definitions A top-level `type` of `object` indicates that the write request body must be a JSON object. The `properties` object contains the available fields, while the `required` array lists the fields that must be included. ```json theme={null} { "type": "object", "properties": { "first_name": { "type": "string", "description": "The employee's first name.", "isRequired": true }, "last_name": { "type": "string", "description": "The employee's last name.", "isRequired": true } }, "required": ["first_name", "last_name"] } ``` Use `isRequired` to review the requirement for an individual field. Use the `required` array to identify all mandatory fields within an object. ### Nested Objects and Arrays For a field with `type: "object"`, nested fields are defined under `properties`. ```json theme={null} "home_location": { "type": "object", "description": "The employee's home address.", "isRequired": false, "properties": { "street_1": { "type": "string", "description": "The first line of the street address.", "isRequired": false }, "city": { "type": "string", "description": "The city of the address.", "isRequired": false } } } ``` For a field with `type: "array"`, the schema for each array element is defined under `items`. ```json theme={null} "earnings": { "type": "array", "description": "The earnings entries for the payroll run.", "isRequired": false, "items": { "type": "object", "properties": { "earning_code": { "type": "string", "isRequired": true }, "amount": { "type": "number", "isRequired": false } }, "required": ["earning_code"] } } ``` ## Integration-Specific Fields Meta API responses can include `integration_params` and `additional_attributes`. These fields serve different purposes. | Field | Structure | Validation | Purpose | | ----------------------- | ---------------- | ---------------------------------- | ------------------------------------------------------------------------------------------ | | `integration_params` | Defined object | Validated and potentially required | Contains parameters required by the underlying integration for the write operation. | | `additional_attributes` | Free-form object | Not defined by the unified schema | Contains integration-specific values that are not represented in Bindbee's unified fields. | ### `integration_params` `integration_params` contains fields required by the underlying integration. Its properties are explicitly defined in the Meta API response and can be required. ```json theme={null} "integration_params": { "type": "object", "description": "Parameters required by the integration for the write operation.", "isRequired": true, "properties": { "pay_statement_type_id": { "type": "string", "isRequired": true } }, "required": ["pay_statement_type_id"] } ``` ### `additional_attributes` `additional_attributes` is a free-form object for fields that are supported by the underlying integration but are not represented in Bindbee's unified model. ```json theme={null} "additional_attributes": { "type": "object", "description": "Additional integration-specific attributes for the write operation.", "isRequired": false } ``` Values included in `additional_attributes` are forwarded to the underlying integration as part of the write request. ## Accepted Values and Validation ### `enum` and `enumInformation` The `enum` keyword lists the values accepted by a field. When available, `enumInformation` provides a description for each accepted value. ```json theme={null} "pay_statement_type_id": { "type": "string", "isRequired": true, "enum": ["51743768", "51743769", "51743758"], "enumInformation": [ { "value": "51743768", "description": "Third-Party Sick" }, { "value": "51743769", "description": "Bonus" }, { "value": "51743758", "description": "Regular" } ] } ``` The request body must use one of the values listed in `enum`. The corresponding entry in `enumInformation` can be used to determine the appropriate value. ### `description` and `format` The `description` keyword explains the purpose of a field. The `format` keyword defines an additional constraint for a string value, such as an email address or UUID. ```json theme={null} "work_email": { "type": "string", "description": "The employee's work email address.", "format": "email", "isRequired": false } ``` ## Constructing the Write Request After reviewing the schema, construct a request body that includes all required fields and conforms to the defined types, formats, and accepted values. For example, if `first_name` and `last_name` are required, the following request body is valid: ```json theme={null} { "first_name": "Jane", "last_name": "Doe", "work_email": "jane@acme.com", "additional_attributes": { "employee_number": "E-1042" } } ``` In this example: * `first_name` and `last_name` satisfy the required field definitions. * `work_email` conforms to the `email` format. * `employee_number` is included as an integration-specific attribute. ## Common Errors ### Meta API Not Implemented for an Integration A Meta API schema may not yet be available for every supported write operation and integration combination. In this case, the Meta endpoint returns the following HTTP status: ```http theme={null} 501 Not Implemented ``` Example response: ```json theme={null} { "detail": "Meta API is not implemented for Time Off in bamboohr. You can request this feature by contacting support@bindbee.dev." } ``` This error indicates that the Meta API schema is not yet available for the selected integration and write operation. To request Meta API support for an additional integration or operation, contact `support@bindbee.dev`. # Webhooks Source: https://docs.bindbee.dev/webhooks/overview With Webhooks, you can receive real-time updates on connector sync statuses, as well as notifications whenever HRIS data is created or modified. This ensures you stay up-to-date with the latest changes and maintain accurate, synchronized information across your systems, enhancing your workflow efficiency and data reliability. ## Create Webhooks To create webhooks, follow the instructions below. **Step 1:** Navigate to the “Webhooks” section from the sidebar under the “Configure” menu. Image **Step 2:** In the “New webhook” section, enter the destination URL where you want to receive the webhook notifications in the “Destination URL” field. Download **Step 3:** Optionally, click the “Test URL” button to ensure that your destination URL is correctly set up and can receive test data. **Step 4:** Provide a meaningful name for your webhook in the “Webhook Name” field. For example, “Employee data.” **Step 5:** Under “Webhook Type,” select the environment for which you want to receive webhook alerts. | Webhook Type | Receives alerts for | Recommended use | | --------------- | ---------------------- | ---------------------------------- | | **Production** | Production connectors | Live customer integrations | | **Development** | Development connectors | Testing and development connectors | > Note: Production and Development webhooks are environment-specific. This means a Production webhook will only receive alerts for connectors created in the [Production environment](https://docs.bindbee.dev/features/environments/production), while a Development webhook will only receive alerts for connectors created in the [Development environment](https://docs.bindbee.dev/features/environments/development). **Step 6:** Choose the events for which you want to receive notifications by selecting the appropriate checkboxes: * **Sync start**: Receive an alert when a connector’s sync starts. * **Sync error**: Get notified when a connector sync fails. * **Sync completed**: Get notified when a connector sync job is successful. * **Employee model change**: Receive an alert when any employee data is modified. * **Data model change**: Receive an alert when any data model is modified. **Step 7:** Once you have filled in all the required fields and selected the notification triggers, click the “Create webhook” button to save your webhook configuration. ## Payload Properties * `webhook`: Information about the webhook that was triggered. * `connector`: Information about the connector associated with the event. * `data`: The affected connector sync status data. * `sync`: Information about the sync job that triggered the event. This object is included in all webhook payloads. * `error`: Information about the sync failure. This object is included only in the `connector_sync_error` webhook. ### Sync information All webhook payloads include a top-level `sync` object in the following format: ```json theme={null} { "sync_id": "019f146e-8396-7c6d-aff3-c5760bf5eefe", "sync_type": "AUTOMATED", "sync_name": "#13" } ``` | Field | Description | | ----------- | --------------------------------------------------------------------- | | `sync_id` | The unique identifier of the sync job. | | `sync_type` | How the sync was triggered. Possible values:`MANUAL` and `AUTOMATED`. | | `sync_name` | The name assigned to the sync job. | ### Sync error information The `connector_sync_error` webhook includes a top-level `error` object in the following format: ```json theme={null} { "failed_at": "2026-07-07T11:21:35.837448+00:00", "message": "A runtime error occurred during the sync process. Bindbee team has been notified.", "detail": {} } ``` | Field | Description | | ----------- | ------------------------------------------------------------------------------------------------------------------------ | | `failed_at` | The timestamp at which the sync job failed. | | `message` | A summary of the sync job error. | | `detail` | Additional details about the error. This is returned as an empty object (`{}`) when no additional details are available. | ## Webhook event types * Connector Sync Started * Connector Synced * Connector Sync Error * Employee Data Changed * Connector Data Modified ### Sample payload for webhook events Receive an alert when the connector sync starts. ```json theme={null} { "webhook": { "webhook_id": "10c9a6b1-4ea8-476e-bdb0-193001a94a8d", "url": "https://webhook-test.com/8e771727c78e3a4017f374fb745313b6", "event": "connector_sync_started" }, "connector": { "id": "018f99e2-ed3a-7f4c-ae97-83d619bdf95c", "display_name": "BreatheHR", "integration_slug": "breathehr", "categories": [ "HRIS" ], "org_name": "Test Org", "origin_id": "user_123", "connector_status": "COMPLETE", "end_user_name": "John Doe", "end_user_email": "abc@xyz.com" }, "data": { "id": "018f99e7-886b-7c9f-a63a-f6fbc9da1f83", "integration_name": "breathehr", "category": "HRIS", "display_status": "Syncing", "last_sync_start_time": "2024-07-22T09:49:30.478384+00:00" }, "sync": { "sync_id": "019f146e-8396-7c6d-aff3-c5760bf5eef8", "sync_type": "AUTOMATED", "sync_name": "#13" } } ``` Receive an alert when the connector sync finishes successfully. ```json theme={null} { "webhook": { "webhook_id": "10c9a6b1-4ea8-476e-bdb0-193001a94a8d", "url": "https://webhook-test.com/8e771727c78e3a4017f374fb745313b6", "event": "connector_synced" }, "connector": { "id": "018f99e2-ed3a-7f4c-ae97-83d619bdf95c", "display_name": "BreatheHR", "integration_slug": "breathehr", "categories": [ "HRIS" ], "org_name": "Test Org", "origin_id": "user_123", "connector_status": "COMPLETE", "end_user_name": "John Doe", "end_user_email": "abc@xyz.com" }, "data": { "id": "018f99e7-886b-7c9f-a63a-f6fbc9da1f83", "integration_name": "breathehr", "category": "HRIS", "display_status": "Done", "last_sync_start_time": "2024-07-22T09:49:22.922083+00:00" }, "sync": { "sync_id": "019f146e-8396-7c6d-aff3-c5760bf5eef8", "sync_type": "AUTOMATED", "sync_name": "#13" } } ``` Receive an alert when the connector sync encounters an error. ```json theme={null} { "webhook": { "webhook_id": "704ec26b-585d-4551-a833-e447e3313c78", "url": "https://play.svix.com/in/kxY1EpRT84F0JIxx43lgfnr2Icr/", "event": "connector_sync_error" }, "connector": { "id": "019ebc1c-953a-7ba9-b599-0e6599e30304", "display_name": "Workday", "integration_slug": "workday", "categories": [ "LMS", "HRIS" ], "org_name": "Bindbeee", "origin_id": "dfgvbbgtfbnytgnhgh", "connector_status": "COMPLETE", "end_user_name": "Coca Cola", "end_user_email": null }, "data": { "id": "018f96ad-459c-7fd5-a24a-2c3a6c1a64e8", "integration_name": "workday", "category": "HRIS", "display_status": "Failed" }, "error": { "failed_at": "2026-07-07T11:21:35.837448+00:00", "message": "A runtime error occurred during the sync process. Bindbee team has been notified.", "detail": {} }, "sync": { "sync_id": "019f3c4f-dbc2-7480-a6d5-f1f789ee05c1", "sync_type": "AUTOMATED", "sync_name": "#6" } } ``` The `detail` field is returned as an empty object (`{}`) when no additional error details are available. Receive an alert when an employee is created or updated. The payload returns the `id` of each employee that was created or modified. ```json theme={null} { "webhook": { "webhook_id": "10c9a6b1-4ea8-476e-bdb0-193001a94a8d", "url": "https://webhook-test.com/8e771727c78e3a4017f374fb745313b6", "event": "employee_data_changed" }, "connector": { "id": "018f99e2-ed3a-7f4c-ae97-83d619bdf95c", "display_name": "BreatheHR", "integration_slug": "breathehr", "categories": [ "HRIS" ], "org_name": "Test Org", "origin_id": "user_123", "connector_status": "COMPLETE", "end_user_name": "John Doe", "end_user_email": "abc@xyz.com" }, "data": [ "0190d9d8-ac9b-76cb-a5ee-df70994e6f3d" ], "sync": { "sync_id": "019f146e-8396-7c6d-aff3-c5760bf5eef8", "sync_type": "AUTOMATED", "sync_name": "#13" } } ``` Receive an alert when a record in any supported data model is created or updated. The payload returns the `id` of each record that was created or modified. ```json theme={null} { "webhook": { "webhook_id": "10c9a6b1-4ea8-476e-bdb0-193001a94a8d", "url": "https://webhook-test.com/8e771727c78e3a4017f374fb745313b6", "event": "connector_data_modified" }, "connector": { "id": "018f99e2-ed3a-7f4c-ae97-83d619bdf95c", "display_name": "BreatheHR", "integration_slug": "breathehr", "categories": [ "HRIS" ], "org_name": "Test Org", "origin_id": "user_123", "connector_status": "COMPLETE", "end_user_name": "John Doe", "end_user_email": "abc@xyz.com" }, "data": { "hris_bank_info": [ "0190d9d8-cf5b-7ce4-b60f-1fd95c04adee" ], "hris_employment": [ "0190d9d9-023d-7259-97e1-b38682dec062" ] }, "sync": { "sync_id": "019f146e-8396-7c6d-aff3-c5760bf5eef8", "sync_type": "AUTOMATED", "sync_name": "#13" } } ``` ## Retry Mechanism When a webhook event is triggered, Bindbee sends a `POST` request to your configured destination URL. If the request fails due to a request or network-layer issue, Bindbee automatically retries the delivery up to 4 times within 60 seconds. Each delivery attempt has a timeout of 10 seconds. Your server should respond within this window to avoid the request being treated as timed out. ### Retry behavior | Property | Value | | --------------- | --------------------------------------------------------- | | Maximum retries | Up to 4 retries | | Retry window | Within 60 seconds | | Request timeout | 10 seconds per attempt | | Request method | `POST` | | Retry trigger | Request/network-layer failures and all non-`2xx`responses | ### Failures that trigger retries Retries are triggered only for request or network-layer failures, including: | Failure category | Examples | | -------------------- | ------------------------------------------------------------------------ | | Timeout errors | Connection timeout, read timeout, write timeout, connection pool timeout | | Connection errors | Connection error, read error, write error, close error | | Protocol errors | Local protocol error, remote protocol error, unsupported protocol error | | Other request errors | Proxy error, response decoding error, too many redirects | ## Security To secure your API endpoint, ensure it verifies that POST requests are genuinely from Bindbee and not from a malicious source, and that payloads haven’t been tampered with during transit. You can achieve this by checking if the `X-Bindbee-Webhook-Signature` field in the request header matches an encoded combination of your organization’s unique webhook signature and the payload of the incoming request. In the Webhooks configuration page, you should find a Security section where you can access your signature key. This key is unique to your organization and can be regenerated if it becomes known to an unauthorized party. ![Webhook Security](https://images.unifyx.dev/documentation_images/signature_key.webp) ```python Python theme={null} import base64 import hashlib import hmac # Replace 'YOUR_WEBHOOK_SIGNATURE_KEY' with the actual key from: # https://app.bindbee.dev/webhooks signature_key = "YOUR_WEBHOOK_SIGNATURE_KEY" request_body = request.body # Ensure the presence of a signature header in the request if "X-Bindbee-Webhook-Signature" not in request.headers: print('Signature header missing; request may not be from Bindbee.') raise # Retrieve the signature header from the request received_signature = request.headers["X-Bindbee-Webhook-Signature"] # Convert the key and request body to bytes key_bytes = signature_key.encode("utf-8") body_bytes = request_body.encode("utf-8") # Create an HMAC digest using SHA-256 hmac_digest = hmac.new(key_bytes, body_bytes, hashlib.sha256).digest() # Encode the digest to base64 encoded_digest = base64.urlsafe_b64encode(hmac_digest).decode() # Use `hmac.compare_digest` to compare the computed digest with the received signature signature_valid = hmac.compare_digest(encoded_digest, received_signature) ``` ```ruby Ruby theme={null} require 'base64' require 'openssl' require 'json' # Replace 'YOUR_WEBHOOK_SIGNATURE_KEY' with the actual key from: # https://app.bindbee.dev/webhooks secret_key = 'YOUR_WEBHOOK_SIGNATURE_KEY' request_body = request.raw_post received_signature = request.env['X-Bindbee-Webhook-Signature'] unless received_signature raise "Signature header missing; request may not be from Bindbee." end # Ensure the payload is properly encoded in UTF-8 request_body.force_encoding('UTF-8') # Compute HMAC digest using SHA-256 hmac_digest = OpenSSL::HMAC.digest(OpenSSL::Digest.new('sha256'), secret_key, request_body) # Base64 encode the HMAC digest encoded_digest = Base64.urlsafe_encode64(hmac_digest) # Use secure comparison to validate the signature valid_signature = ActiveSupport::SecurityUtils.secure_compare(encoded_digest, received_signature) ``` ```javascript Node theme={null} const crypto = require("crypto"); const express = require("express"); const app = express(); // Replace 'YOUR_WEBHOOK_SIGNATURE_KEY' with the actual key from: // https://app.bindbee.dev/webhooks // Middleware to capture raw body for HMAC verification app.use( express.json({ verify: (req, res, buf) => { req.rawBody = buf.toString(); }, }), ); const webhookSecret = "YOUR_WEBHOOK_SIGNATURE_KEY"; const rawRequestBody = request.rawBody; const receivedSignature = request.headers["X-Bindbee-Webhook-Signature"]; if (!receivedSignature) { console.log("Missing webhook signature; request may not be from Bindbee."); } // Ensure the request body is properly encoded const utf8Body = Buffer.from(rawRequestBody, "utf-8").toString(); // Create an HMAC digest using SHA-256 const hmacDigest = crypto .createHmac("sha256", webhookSecret) .update(utf8Body) .digest("base64") .replace(/\+/g, "-") .replace(/\//g, "_"); // Use crypto.timingSafeEqual to compare the signatures securely const expectedSignature = Buffer.from(receivedSignature, "utf-8"); const calculatedSignature = Buffer.from(hmacDigest, "utf-8"); const isSignatureValid = Buffer.byteLength(expectedSignature) === Buffer.byteLength(calculatedSignature) && crypto.timingSafeEqual(expectedSignature, calculatedSignature); console.log(isSignatureValid); ``` ## Webhook Visibility The webhook logs are easily visible on the webhooks tab at [Bindbee](https://app.bindbee.dev/logs/webhooks) dashboard. The specific webhook logs will be visible on the individual webhook page.