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

# Connect your own devices

> Create a KODE API connector in a building, ingest your devices and points, and track the ingest jobs through the KODE API.

A KODE API connector lets you bring devices and points from your own system into a building in KODE OS. You create the connector, send the devices and points you want, and follow each request as a background job. This guide takes you from a building to ingested devices and points.

To send point values after the points exist, continue with [Ingest device and point data](https://docs.kodelabs.com/products/kode-api/guides/ingest-device-data.md).

Each linked endpoint can switch the sample between cURL, JavaScript, Python, and PowerShell, or send the request for you.

> [!WARNING]
>
> Every request in this guide that is not a read adds data to a real building in your organization. Ingested devices and points appear in KODE OS next to the rest of the building data. Use a test building first.

## Before you begin

You need:

* An access token. See the [quickstart](https://docs.kodelabs.com/products/kode-api/quickstart.md).
* A service account whose KODE OS role allows the actions you use:

  | Step | Actions |
  | - | - |
  | Read buildings, jobs, and connectors | `Get Buildings`, `Get connectors` |
  | Create a building | `Create building` |
  | Set the status of a building | `Publish or unpublish building` |
  | Create a connector | `Create KODE API connector` |
  | Ingest devices and points | `Ingest connector devices`, `Ingest connector points` |

## Choose a building

You can use a building that already exists or create one.

> [tab] Use an existing building
>
> Call [List buildings](https://docs.kodelabs.com/products/kode-api/api-reference/buildings/list-buildings.md) and note the `_id` of the building to use as `buildingId`.
>
> ```bash theme={"theme":{"light":"github-light","dark":"github-dark"}}
> curl "https://api.kodelabs.com/kodeos/api/v1/buildings" \
>   --header "Authorization: Bearer ${KODE_ACCESS_TOKEN}"
> ```

> [tab] Create a building
>
> Send the required fields to [Create a building](https://docs.kodelabs.com/products/kode-api/api-reference/buildings/create-a-building.md).
>
> ```bash theme={"theme":{"light":"github-light","dark":"github-dark"}}
> curl --request POST \
>   "https://api.kodelabs.com/kodeos/api/v1/buildings" \
>   --header "Authorization: Bearer ${KODE_ACCESS_TOKEN}" \
>   --header "Content-Type: application/json" \
>   --data '{
>     "name": "API test building",
>     "primaryFunction": "Office",
>     "baseTemp": 68,
>     "address": {
>       "address1": "1600 Amphitheatre Parkway",
>       "city": "Mountain View",
>       "state": "CA",
>       "zip": "94043",
>       "country": "US"
>     }
>   }'
> ```
>
> Keep the `_id` from the response as `buildingId`.
>
> * `name` is up to 50 characters and must be unique in your organization, ignoring case. A repeated name returns `409`.
>
> * `primaryFunction` must be one of the values listed on the endpoint page, with the same capitalization. Any other value returns `400`.
>
> * `baseTemp` is in °F.
>
> * The API looks up the address. An address that cannot be located returns `400`.
>
> * A new building is unpublished. To change it, call [Set the status of a building](https://docs.kodelabs.com/products/kode-api/api-reference/buildings/set-the-status-of-a-building.md) with `PUBLISHED` or `UNPUBLISHED`.
>
> > [!WARNING]
> >
> > The API cannot delete a building, and the name stays taken until someone removes the building in KODE OS. Create buildings sparingly, and ask your KODE project team to remove a test building when you are done.
>
> > [!NOTE]
> >
> > A new building has every product turned off, so calls that name it return `403` until the `api` product is enabled for it. The API cannot enable products. Your KODE project team enables the `api` product for a new building by hand. Ask them to enable it before you continue, and allow for that step if you automate building creation.
>
> To change the building later, use [Update a building](https://docs.kodelabs.com/products/kode-api/api-reference/buildings/update-a-building.md). Fields you leave out keep their stored values. You cannot clear a field.

If a call on the building returns `403`, the building does not exist, is not available to your service account, or has no `api` product enabled. The API gives the same answer in each case.

## Ingest devices and points

**1. Check for an existing connector**

Call [List connectors](https://docs.kodelabs.com/products/kode-api/api-reference/connectors/list-connectors.md). The list holds every connector in the building. A connector you can ingest into has `integrationType` set to `API_KODE`.

```bash theme={"theme":{"light":"github-light","dark":"github-dark"}}
curl "https://api.kodelabs.com/kodeos/api/v1/buildings/${BUILDING_ID}/connectors" \
  --header "Authorization: Bearer ${KODE_ACCESS_TOKEN}"
```

If one fits, note its `_id` as `connectorId` and go to the ingest steps. Otherwise create one.

**2. Create a KODE API connector**

Send a `name` to [Create a KODE API connector](https://docs.kodelabs.com/products/kode-api/api-reference/connectors/create-a-kode-api-connector.md).

```bash theme={"theme":{"light":"github-light","dark":"github-dark"}}
curl --request POST \
  "https://api.kodelabs.com/kodeos/api/v1/buildings/${BUILDING_ID}/connectors/kode-api" \
  --header "Authorization: Bearer ${KODE_ACCESS_TOKEN}" \
  --header "Content-Type: application/json" \
  --data '{ "name": "Building automation system" }'
```

Keep the `_id` from the response as `connectorId`. The name is free text. The API does not reject a repeated name, so a second request with the same name creates a second connector.

The integrations endpoints call a connector a data source, and its `_id` is the `datasourceId` they use.

**3. Ingest the devices**

Send your devices to [Ingest devices through a connector](https://docs.kodelabs.com/products/kode-api/api-reference/connectors/ingest-devices-through-a-connector.md). Use stable identifiers from your system for `deviceExternalId`.

```bash theme={"theme":{"light":"github-light","dark":"github-dark"}}
curl --request POST \
  "https://api.kodelabs.com/kodeos/api/v1/buildings/${BUILDING_ID}/connectors/${CONNECTOR_ID}/ingest/devices" \
  --header "Authorization: Bearer ${KODE_ACCESS_TOKEN}" \
  --header "Content-Type: application/json" \
  --data '{
    "items": [
      {
        "deviceExternalId": "ahu-01",
        "deviceExternalPath": "/site/roof/ahu-01",
        "deviceName": "AHU-01",
        "deviceLocation": "Roof"
      },
      {
        "deviceExternalId": "ahu-02",
        "deviceExternalPath": "/site/roof/ahu-02",
        "deviceName": "AHU-02"
      }
    ]
  }'
```

The response has status `202` and holds a job:

```json theme={"theme":{"light":"github-light","dark":"github-dark"}}
{
  "_id": "6710a1f4c2d0e5a9b8f3c7d1",
  "name": "Ingest connector devices",
  "status": "PROCESSING"
}
```

Keep the job `_id` as `jobId`. Send up to 1,000 devices in one request. Sending a `deviceExternalId` again updates the name and location of that device. It does not create a duplicate.

**4. Wait for the devices job**

Call [Get a job](https://docs.kodelabs.com/products/kode-api/api-reference/buildings/get-a-job.md) every few seconds until `status` is `COMPLETED` or `FAILED`.

```bash theme={"theme":{"light":"github-light","dark":"github-dark"}}
curl "https://api.kodelabs.com/kodeos/api/v1/buildings/${BUILDING_ID}/jobs/${JOB_ID}" \
  --header "Authorization: Bearer ${KODE_ACCESS_TOKEN}"
```

| `status` | What to do |
| - | - |
| `PENDING` or `PROCESSING` | Wait and call again. |
| `COMPLETED` | Go to the next step. |
| `FAILED` | Read `errorMessage`, fix the request, and send it again. |

Polling counts toward your [rate limits](https://docs.kodelabs.com/products/kode-api/fundamentals.md#rate-limits), so wait a few seconds between calls.

**5. Ingest the points**

Points belong to devices, so ingest the devices first. Each point item names its parent device with the same `deviceExternalId` and `deviceExternalPath` you sent in the previous step. Send the items to [Ingest points through a connector](https://docs.kodelabs.com/products/kode-api/api-reference/connectors/ingest-points-through-a-connector.md).

```bash theme={"theme":{"light":"github-light","dark":"github-dark"}}
curl --request POST \
  "https://api.kodelabs.com/kodeos/api/v1/buildings/${BUILDING_ID}/connectors/${CONNECTOR_ID}/ingest/points" \
  --header "Authorization: Bearer ${KODE_ACCESS_TOKEN}" \
  --header "Content-Type: application/json" \
  --data '{
    "items": [
      {
        "deviceExternalId": "ahu-01",
        "deviceExternalPath": "/site/roof/ahu-01",
        "pointExternalId": "supply-air-temp",
        "pointExternalPath": "/site/roof/ahu-01/supply-air-temp",
        "pointName": "Supply Air Temp",
        "pointKind": "Number",
        "pointMinVal": 0,
        "pointMaxVal": 150
      },
      {
        "deviceExternalId": "ahu-01",
        "deviceExternalPath": "/site/roof/ahu-01",
        "pointExternalId": "fan-command",
        "pointExternalPath": "/site/roof/ahu-01/fan-command",
        "pointName": "Fan Command",
        "pointKind": "Bool",
        "pointWritable": true
      }
    ]
  }'
```

Use these rules:

* `pointKind` is `Number`, `Str`, or `Bool`, with that capitalization.
* `pointUnit` is the `_id` of an ontology unit. An unknown unit returns `400`.
* `pointEnum` is one comma-separated string, such as `off,on,auto`.
* `pointMinVal` cannot be higher than `pointMaxVal`.
* `pointWritable` defaults to `false`.

If a parent device was not ingested on this connector, the request returns `400` and names the missing `deviceExternalId` values. Fix the request and send it again. Then wait for the job as you did for the devices.

**6. Confirm the result**

A completed job tells you that it ran to the end. It does not list what it stored. Read the data back:

* [List building devices](https://docs.kodelabs.com/products/kode-api/api-reference/devices/list-building-devices.md) shows the ingested devices.
* [List building points](https://docs.kodelabs.com/products/kode-api/api-reference/points/list-building-points.md) shows the ingested points and their parent devices.
* [Get a connector](https://docs.kodelabs.com/products/kode-api/api-reference/connectors/get-a-connector.md) shows the connector.
* [Get connector status](https://docs.kodelabs.com/products/kode-api/api-reference/connectors/get-connector-status.md) shows the last connection status KODE OS recorded. A new KODE API connector has none, so the response is an empty object. Ingesting devices or points does not change it.

## Send point values

After the points exist, send their values with the timeseries endpoints. See [Ingest device and point data](https://docs.kodelabs.com/products/kode-api/guides/ingest-device-data.md), which covers current values, history, and rate limits.

## Update what you sent

Send a device again with the same `deviceExternalId` to change its name or location. Send a point again with the same `pointExternalId` and the API does not create a second point.

## Limits

| Limit | Value |
| - | - |
| Devices per ingest request | 1,000 |
| Points per ingest request | 1,000 |
| Building `name` length | 50 characters |
| `pointName` length | 255 characters |
| Connectors that accept ingest | KODE API connectors (`integrationType` `API_KODE`) only |

Ingest requests count toward your [rate limits](https://docs.kodelabs.com/products/kode-api/fundamentals.md#rate-limits).

## Troubleshooting

| Response | Likely cause |
| - | - |
| `403` | The building does not exist or is not assigned to your service account, the `api` product is not enabled for it, or the role lacks the action. A building you just created needs your KODE project team to enable the `api` product first. |
| `404` on a connector or job | The id is not in this building. A connector or job of another building returns `404`. |
| `400` on ingest points | A parent device, a unit, or a `pointKind` is not valid, or a minimum is higher than its maximum. Read `error.message`. |
| `400` on ingest | The connector is not a KODE API connector, `items` is empty or holds more than 1,000 items, or an item lacks a required member. |
| `409` on create or update | Another building in your organization has this name. |
| `409` on a status change | The building already has this status. |


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.