> ## Documentation Index
> Fetch the complete documentation index at: https://docs.bindbee.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# Record identity

> Choose which of the two identifiers on a record to store.

Every object the Unified API returns carries two identifiers:

```json theme={null}
{
  "id": "018b18ef-c487-703c-afd9-0ca478ccd9d6",
  "remote_id": "3235005483341316245",
}
```

They are not interchangeable. Picking the wrong one is one of the more common sources of subtle bugs in an integration, so it is worth being deliberate about which goes where in your database.

<Tip>
  The short version: **store both, key on `id`, index `remote_id` for lookups and support.**
</Tip>

## `id`

`id` is a UUID that Bindbee assigns when it first syncs a record. It is the identifier the Unified API is built around.

**Properties**

* **Stable across syncs.** The same record keeps the same `id` from one sync to the next.
* **Consistent in shape.** A UUID on every connector, so your schema does not accommodate a different format per HRIS.
* **Scoped to your environment.** Development and Production hold separate data, so the same employee has different IDs in each - see [Environments](/get-started/environments).

**Where you use it**

* **Get-by-id endpoints.** `GET /api/hris/v1/employees/{id}` takes the Bindbee `id`.
* **The `ids` filter.** Pass one or more, comma separated with no spaces, to fetch a specific set in a single call.
* **Relation fields.** `manager`, `company`, `groups`, `work_locations`, `pay_group` and `payroll_run_calendar` all return Bindbee IDs. An employee's `manager` resolves against the Employees endpoint by `id`.
* **`expand`.** Expanding a relation resolves it through Bindbee IDs - see [Expand](/guides/reading-writing/reading-data/expand).

Because relations are expressed in Bindbee IDs, `id` is the only identifier that lets you traverse the graph.

## `remote_id`

`remote_id` is the identifier the source system uses. Bindbee stores it as a string and passes it through untouched.

**Properties**

* **Format varies by system.** A numeric string in one HRIS, a UUID in another, an alphanumeric code in a third. Never parse it, pad it, cast it to an integer, or assume a length.
* **Nullable.** Some systems do not expose a stable API identifier for every object type. When that happens, `remote_id` is `null`. Any logic that depends on it needs a fallback.
* **Unique only within a connector.** Two customers on two different HRIS instances can both have an employee with `remote_id` of `1`. It is not safe as a global key, and the same person under two connectors is legitimately two records - see [Trace a duplicate record](/guides/troubleshooting/missing-records).

**Where you use it**

* **Reconciliation.** Matching a Bindbee record back to a record in the customer's own system, or against a file feed the customer sends you.
* **Support and debugging.** When a customer reports that one employee's data looks wrong, `remote_id` is what lets you and Bindbee find the same record in the source system.
* **Filtered lookups.** The Employees endpoint accepts `remote_id` as a filter when you know the third-party ID but not the Bindbee one:

```bash theme={null}
curl -X GET "https://api.bindbee.dev/api/hris/v1/employees?remote_id=3235005483341316245" \
  -H "Authorization: Bearer <BINDBEE_API_KEY>" \
  -H "X-Connector-Token: <CONNECTOR_TOKEN>"
```

The connector token scopes that lookup to a single connector, which is what makes the query unambiguous.

## `employee_number`

The Employee model carries a third identifier, and confusing it with `remote_id` is the usual mistake:

| Field             | Audience                                     | Example                                |
| ----------------- | -------------------------------------------- | -------------------------------------- |
| `id`              | Your code and the Bindbee API                | `018b18ef-c487-703c-afd9-0ca478ccd9d6` |
| `remote_id`       | The third-party **API**                      | `3235005483341316245`                  |
| `employee_number` | The **humans** using the third-party product | `000000168`                            |

`employee_number` is what an HR admin sees in their HRIS interface, and what a customer will quote at you in a support ticket. It is frequently a different value from `remote_id`, and it is neither guaranteed unique nor stable, and an admin can edit it. Use it for display and for matching against what a customer tells you, never as a key. It has its own filter:

```bash theme={null}
curl -X GET "https://api.bindbee.dev/api/hris/v1/employees?employee_number=000000168" \
  -H "Authorization: Bearer <BINDBEE_API_KEY>" \
  -H "X-Connector-Token: <CONNECTOR_TOKEN>"
```

## How to store them

A workable shape for the employees table on your side:

| Column                                               | Source            | Notes                                                                          |
| ---------------------------------------------------- | ----------------- | ------------------------------------------------------------------------------ |
| `bindbee_id`                                         | `id`              | Primary key. All foreign keys point here.                                      |
| `connector_token` or an internal connector reference | your own          | Scopes everything below it                                                     |
| `remote_id`                                          | `remote_id`       | Indexed, nullable, unique only in combination with the connector               |
| `employee_number`                                    | `employee_number` | Display and support only                                                       |
| `modified_at`                                        | `modified_at`     | Drives your [delta syncs](/guides/reading-writing/reading-data/modified-after) |

## Frequently Asked Questions

<AccordionGroup>
  <Accordion title="Why is remote_id null on some records?">
    More common on sub-objects than on employees. Fall back to `id` for anything functional, and hide the field rather than rendering it empty. To see the source's own scheme, read the payload with `include_raw_data=true` - see [Raw Data](/guides/reading-writing/raw-data).
  </Accordion>

  <Accordion title="Can I write back using remote_id?">
    No. A write that references an existing record addresses it by Bindbee `id`. If you hold a third-party identifier, resolve it with the `remote_id` filter first, then act on the `id` you get back.
  </Accordion>

  <Accordion title="Do IDs change when a connector is relinked?">
    **No.** Relinking keeps the connector and its history, so stored `id` values stay valid - which is why it is the right recovery from `RELINK_NEEDED`.

    Deleting and issuing a fresh link is different: it mints new identities and breaks every ID you stored. `remote_id` is what matches old rows to new ones - see [Update credentials](/guides/troubleshooting/connector-relink).
  </Accordion>
</AccordionGroup>

## Related

<CardGroup cols={2}>
  <Card title="Reading data" icon="funnel" href="/guides/reading-writing/reading-data">
    Filters, `expand` and `modified_after` on list endpoints.
  </Card>

  <Card title="Get Employees" icon="users" href="/hris/employee/get-employees">
    The full Employee schema and every supported filter.
  </Card>

  <Card title="Trace a duplicate record" icon="list" href="/guides/troubleshooting/missing-records">
    Diagnosing a specific pair that should have been one record.
  </Card>

  <Card title="Custom fields" icon="table" href="/guides/extending/custom-fields">
    Surface additional identifiers from the raw payload as fields.
  </Card>
</CardGroup>
