

# Jobs API Reference

API endpoints for managing jobs.

## Lock Jobs

Bridge Proxied Jobs send commands to smart locks through a Bridge device. The Bridge acts as an intermediary that communicates with locks via Bluetooth while your application communicates with the Bridge via the API.

Send these jobs with [Create a Bridge Proxied Job](#create-a-bridge-proxied-job).

### Job Creation

Create a job by making a `POST` request to:

`/devices/{deviceId}/jobs/bridges/{bridgeId}`

**Request Parameters**

| Parameter | Type | Description |
| --- | --- | --- |
| jobType | number | Command type identifier (see Job Types) |
| jobData | object | Command-specific parameters |

**Response**

```json
{
  "jobId": "string"
}
```

**Job Types**

| Job Type | ID |
| --- | --- |
| Lock | 1 |
| Unlock | 2 |
| Create Custom PIN code | 4 |
| Delete PIN code | 5 |
| Get Battery Level | 9 |
| Get Device Status | 10 |
| Get Activity Logs | 15 |

### Command Details

#### Lock

Locks the device and optionally synchronizes its internal clock.

| Parameter | Type | Description | Required |
| --- | --- | --- | --- |
| lockTime | ISO_DATE | Synchronize the lock's clock | No |

```json
{
  "jobType": 1,
  "jobData": {
    "lockTime": "2024-01-01T12:00:00+00:00"
  }
}
```

#### Unlock

Unlocks the device and optionally synchronizes its internal clock.

| Parameter | Type | Description | Required |
| --- | --- | --- | --- |
| lockTime | ISO_DATE | Synchronize the lock's clock | No |

```json
{
  "jobType": 2,
  "jobData": {
    "lockTime": "2024-01-01T12:00:00+00:00"
  }
}
```

#### Create Custom PIN Code

Creates a PIN code with specific access permissions and time restrictions.

Devices/locks supporting PIN are listed in [Supported Devices](/home/supported-devices).

| Parameter | Type | Description | Required |
| --- | --- | --- | --- |
| accessName | string | Display name for the PIN code, up to 60 characters | Yes |
| pin | string | 4-6 digit PIN code | Yes |
| pinType | number | PIN type (see table below) | Yes |
| startDate | string | When PIN becomes active (ISO_DATE format) | Yes |
| endDate | string | When PIN expires (ISO_DATE format) | Duration only |

**PIN Types**

| Type | ID | Description |
| --- | --- | --- |
| One-Time | 1 | Valid for 24 hours from start date |
| Permanent | 2 | Valid indefinitely from start date |
| Duration | 4 | Valid between start and end dates |

```json
{
  "jobType": 4,
  "jobData": {
    "accessName": "Guest PIN",
    "pin": "123456",
    "pinType": 4,
    "startDate": "2024-01-01T12:00:00+00:00",
    "endDate": "2024-01-01T16:00:00+00:00"
  }
}
```

One-Time PIN (24-hour validity):

```json
{
  "jobType": 4,
  "jobData": {
    "accessName": "OTP PIN",
    "pin": "123456",
    "pinType": 1,
    "startDate": "2024-01-01T00:00:00+00:00"
  }
}
```

#### Delete PIN Code

Removes a PIN code from the lock. An unactivated AlgoPIN with a start date more than 256 hours after its creation cannot be deleted.

| Parameter | Type | Description | Required |
| --- | --- | --- | --- |
| pin | string | PIN code to be deleted, 4-9 digits | Yes |

```json
{
  "jobType": 5,
  "jobData": {
    "pin": "123456"
  }
}
```

#### Get Battery Level

Retrieves the lock's current battery level as a percentage.

```json
{
  "jobType": 9
}
```

#### Get Device Status

Queries the lock's current operational status.

```json
{
  "jobType": 10
}
```

#### Get Activity Logs

Downloads activity logs from the lock. Optionally synchronizes the lock's clock. Activity logs are deleted from the lock after retrieval.

| Parameter | Type | Description | Required |
| --- | --- | --- | --- |
| lockTime | ISO_DATE | Synchronize the lock's clock | No |

```json
{
  "jobType": 15,
  "jobData": {
    "lockTime": "2024-01-01T12:00:00+00:00"
  }
}
```

### Error Status Codes

| Status Code | Description | Applies To |
| --- | --- | --- |
| 400 | Request validation failed, e.g. `job type not supported. see documentation for supported job type(s)` or `pin type not supported. see documentation for supported pin type(s)` | All |
| 401 | Missing, invalid or expired access token | All |
| 402 | The igloohome API trial has ended, or the billing grace period has expired | All |
| 403 | The token lacks the scope for this job type | All |
| 404 | Bridge or device not found | All |
| 406 | `PIN (123456) is already taken. Please try a different pin` | Create Custom PIN code |
| 406 | `Lock is not linked to bridge` | All |
| 406 | `Unable to contact bridge as it appears to be offline` | All |
| 415 | Missing `Content-Type: application/json` | All |
| 500 | Unexpected server error | All |

## Bridge Jobs

These jobs target the Bridge device itself, not a lock.

Send these jobs with [Create a Bridge Targeted Job](#create-a-bridge-targeted-job).

### Job Creation

Create a job by making a `POST` request to:

`/devices/{bridgeId}/jobs`

`bridgeId` is the Bridge's device ID.

| Parameter | Type |
| --- | --- |
| jobType | integer, see Job Types for a full list |

```json
{
  "jobType": 10
}
```

**Response**

```json
{
  "jobId": "string"
}
```

**Job Types**

| Job Type | Number |
| --- | --- |
| Get Device Status | 10 |

### Job Details

#### Get Device Status (Bridge Diagnostic)

```json
{
  "jobType": 10
}
```

Once the job completes, you receive a webhook with the result. See [Get Device Status](/home/bridge-events#get-device-status) for the payload.

**Rate limit:** one Get Device Status job per bridge every 5 minutes per user. Further requests return `429` with a `Retry-After` header (seconds).

### Error Status Codes

| Status Code | Message (API Response) | Description | Applies To |
| --- | --- | --- | --- |
| 400 | Bad Request | The request was unacceptable, often due to a missing or invalid parameter (e.g., `<field>` is required). | All |
| 401 | Unauthorized | No valid API key or access token was provided in the request header. | All |
| 402 | Payment Required (Various Messages) | The API trial has ended or the payment method was declined. Please verify billing details. | All |
| 403 | Forbidden | The authenticated user does not have the necessary permissions (scopes) for this resource. | All |
| 404 | Not Found | The requested resource does not exist or the endpoint URL is incorrect. | All |
| 405 | Method Not Allowed | The HTTP method used is not supported by this endpoint. | All |
| 406 | Unable to contact bridge as it appears to be offline | Unable to contact the bridge. Ensure the physical hardware is powered on and connected. | All |
| 415 | Unsupported Media Type | The Content-Type header must be set to application/json. | N/A |
| 429 | Too many requests. Please try again after X seconds. | Rate limit exceeded: one job per bridge every 5 minutes per user. Wait for the `Retry-After` seconds. | All |
| 500 | Internal Server Error | An unexpected error occurred on our end. Please retry later or contact support. | All |

## Create a Bridge Targeted Job

`POST` `https://api.igloohome.co/home/devices/{bridgeId}/jobs`

Creates a job that targets a Bridge device directly, not a lock via
a bridge. Supports only `jobType` 10 (Get Device Status).

Rate limit: one `GET_DEVICE_STATUS` job per bridge every 5 minutes
per user (except for internal igloohome accounts).

### Path Parameters

| Parameter | Type | Required |
| --- | --- | --- |
| bridgeId | string | Yes |

### Body Parameters

Content-Type: `application/json`

- **`jobType`** `integer` *required*\
  10 = Get Device Status. The only supported value.
  - Allowed values: `10`

### Request Example

```bash
curl -X POST "https://api.igloohome.co/home/devices/{bridgeId}/jobs" \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
  -H "Accept: application/json" \
  -H "Content-Type: application/json" \
  -d '{
    "jobType": "value"
  }'
```

```go
package main

import (
	"bytes"
	"fmt"
	"io"
	"net/http"
)

func main() {
	url := "https://api.igloohome.co/home/devices/{bridgeId}/jobs"
	payload := []byte(`{
		"jobType": "value"
	}`)

	req, _ := http.NewRequest("POST", url, bytes.NewBuffer(payload))
	req.Header.Set("Content-Type", "application/json")
	req.Header.Set("Authorization", "Bearer YOUR_ACCESS_TOKEN")
	req.Header.Set("Accept", "application/json")

	res, _ := http.DefaultClient.Do(req)
	defer res.Body.Close()

	body, _ := io.ReadAll(res.Body)
	fmt.Println(string(body))
}
```

```javascript
const res = await fetch("https://api.igloohome.co/home/devices/{bridgeId}/jobs", {
  method: "POST",
  headers: {
    "Authorization": "Bearer YOUR_ACCESS_TOKEN",
    "Accept": "application/json",
    "Content-Type": "application/json"
  },
  body: JSON.stringify({
    "jobType": "value"
  })
});

const data = await res.json();
console.log(data);
```

```python
import requests

url = "https://api.igloohome.co/home/devices/{bridgeId}/jobs"
headers = {
    "Authorization": "Bearer YOUR_ACCESS_TOKEN",
    "Accept": "application/json"
}
payload = {
    "jobType": "value"
}
response = requests.post(url, headers=headers, json=payload)
print(response.json())
```

```php
<?php
$curl = curl_init();

curl_setopt_array($curl, [
    CURLOPT_URL => "https://api.igloohome.co/home/devices/{bridgeId}/jobs",
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_CUSTOMREQUEST => "POST",
    CURLOPT_HTTPHEADER => [
        "Authorization: Bearer YOUR_ACCESS_TOKEN",
        "Accept: application/json",
        "Content-Type: application/json"
    ],
    CURLOPT_POSTFIELDS => json_encode([
        "jobType" => "value"
    ])
]);

$response = curl_exec($curl);
curl_close($curl);

echo $response;
```

```kotlin
val client = OkHttpClient()

val mediaType = "application/json".toMediaType()
val body = """
    {
        "jobType": "value"
    }
""".trimIndent().toRequestBody(mediaType)

val request = Request.Builder()
    .url("https://api.igloohome.co/home/devices/{bridgeId}/jobs")
    .method("POST", body)
    .addHeader("Authorization", "Bearer YOUR_ACCESS_TOKEN")
    .addHeader("Accept", "application/json")
    .addHeader("Content-Type", "application/json")
    .build()

val response = client.newCall(request).execute()
println(response.body?.string())
```

```swift
import Foundation

var request = URLRequest(url: URL(string: "https://api.igloohome.co/home/devices/{bridgeId}/jobs")!)
request.httpMethod = "POST"
request.setValue("Bearer YOUR_ACCESS_TOKEN", forHTTPHeaderField: "Authorization")
request.setValue("application/json", forHTTPHeaderField: "Accept")
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
let payload: [String: Any] = [
    "jobType": "value"
]
request.httpBody = try? JSONSerialization.data(withJSONObject: payload)

let task = URLSession.shared.dataTask(with: request) { data, response, error in
    if let data = data {
        print(String(data: data, encoding: .utf8) ?? "")
    }
}
task.resume()
```

### Responses

`200 OK`
`400 Bad Request`
`401 Unauthorized, invalid or expired authorization token`
`402 Response`
`403 Forbidden, accessToken scope must contain 'igloohomeapi/get-device-status-bridge-proxied-job'`
`404 Bridge Not Found`
`406 '{ error: 'Unable to contact bridge as it appears to be offline' }'`
`415 Unsupported Media Type`
`429 Too Many Requests. '{ error: 'Too many requests. Please try again after N seconds.' }'. The response includes a 'Retry-After' header (seconds).`
`500 Internal Server Error`

**Response body — `200`:**

- **`jobId`** `string`

**Response body — `400`:**

- **`error`** `string`

**Response body — `402`:**

- **`error`** `string`

**Response body — `406`:**

- **`error`** `string`

**Response body — `429`:**

- **`error`** `string`

**Example response — `200`:**

```json
{
  "jobId": "6345191a82bcfc12a921bdb6"
}
```

**Example response — `400`:**

```json
{
  "error": "job type not supported. see documentation for supported job type(s)"
}
```

**Example response — `402`:**

```json
{
  "error": "30 days igloohome API trial has ended"
}
```

**Example response — `406`:**

```json
{
  "error": "Unable to contact bridge as it appears to be offline"
}
```

**Example response — `429`:**

```json
{
  "error": "Too many requests. Please try again after 45 seconds."
}
```

## Create a Bridge Proxied Job

`POST` `https://api.igloohome.co/home/devices/{deviceId}/jobs/bridges/{bridgeId}`

Creates a job for a lock via a Bridge device.

#### Supported Job Types
|Job Type|Number|
|--|--|
|Lock|1|
|Unlock|2|
|Create Custom PIN code (duration) |4|
|Delete PIN code|5|
|Get Battery Level|9|
|Get Device Status|10|
|Get Activity Logs|15|

`jobData` is only required for `jobType` 4 (Create Custom PIN code) and 5
(Delete PIN code).

|jobType|Required jobData fields|
|--|--|
|4 (Create Custom PIN code)|`accessName`, `pin`, `pinType`, `startDate`, and `endDate` (only when `pinType` is 4/DURATION)|
|5 (Delete PIN code)|`pin` only|

### Path Parameters

| Parameter | Type | Required |
| --- | --- | --- |
| deviceId | string | Yes |
| bridgeId | string | Yes |

### Body Parameters

Content-Type: `application/json`

- **`jobType`** `integer` *required*\
  See the job type table above
  - Allowed values: `1`, `2`, `4`, `5`, `9`, `10`, `15`
- **`jobData`** `object`\
  Shape depends on `jobType`. See the table above. Omit for jobTypes other than 4 and 5.
  - **`accessName`** `string`\
    Required when `jobType` is 4 (Create Custom PIN code).
  - **`pinType`** `integer`\
    1 = One-Time, 2 = Permanent, 4 = Duration. Required when `jobType` is 4.
    - Allowed values: `1`, `2`, `4`
  - **`pin`** `string`\
    4-6 digits when `jobType` is 4 (Create Custom PIN code); 4-9 digits when `jobType` is 5 (Delete PIN code).
  - **`startDate`** `string<date-time>`\
    Required when `jobType` is 4. Must be in format `YYYY-MM-DDTHH:mm:ss+hh:mm`.
  - **`endDate`** `string<date-time>`\
    Required when `jobType` is 4 and `pinType` is 4 (Duration). Must be in format `YYYY-MM-DDTHH:mm:ss+hh:mm` and after `startDate`.

### Request Example

```bash
curl -X POST "https://api.igloohome.co/home/devices/{deviceId}/jobs/bridges/{bridgeId}" \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
  -H "Accept: application/json" \
  -H "Content-Type: application/json" \
  -d '{
    "jobData": "value",
    "jobType": "value"
  }'
```

```go
package main

import (
	"bytes"
	"fmt"
	"io"
	"net/http"
)

func main() {
	url := "https://api.igloohome.co/home/devices/{deviceId}/jobs/bridges/{bridgeId}"
	payload := []byte(`{
		"jobData": "value",
		"jobType": "value"
	}`)

	req, _ := http.NewRequest("POST", url, bytes.NewBuffer(payload))
	req.Header.Set("Content-Type", "application/json")
	req.Header.Set("Authorization", "Bearer YOUR_ACCESS_TOKEN")
	req.Header.Set("Accept", "application/json")

	res, _ := http.DefaultClient.Do(req)
	defer res.Body.Close()

	body, _ := io.ReadAll(res.Body)
	fmt.Println(string(body))
}
```

```javascript
const res = await fetch("https://api.igloohome.co/home/devices/{deviceId}/jobs/bridges/{bridgeId}", {
  method: "POST",
  headers: {
    "Authorization": "Bearer YOUR_ACCESS_TOKEN",
    "Accept": "application/json",
    "Content-Type": "application/json"
  },
  body: JSON.stringify({
    "jobData": "value",
    "jobType": "value"
  })
});

const data = await res.json();
console.log(data);
```

```python
import requests

url = "https://api.igloohome.co/home/devices/{deviceId}/jobs/bridges/{bridgeId}"
headers = {
    "Authorization": "Bearer YOUR_ACCESS_TOKEN",
    "Accept": "application/json"
}
payload = {
    "jobData": "value",
    "jobType": "value"
}
response = requests.post(url, headers=headers, json=payload)
print(response.json())
```

```php
<?php
$curl = curl_init();

curl_setopt_array($curl, [
    CURLOPT_URL => "https://api.igloohome.co/home/devices/{deviceId}/jobs/bridges/{bridgeId}",
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_CUSTOMREQUEST => "POST",
    CURLOPT_HTTPHEADER => [
        "Authorization: Bearer YOUR_ACCESS_TOKEN",
        "Accept: application/json",
        "Content-Type: application/json"
    ],
    CURLOPT_POSTFIELDS => json_encode([
        "jobData" => "value",
        "jobType" => "value"
    ])
]);

$response = curl_exec($curl);
curl_close($curl);

echo $response;
```

```kotlin
val client = OkHttpClient()

val mediaType = "application/json".toMediaType()
val body = """
    {
        "jobData": "value",
        "jobType": "value"
    }
""".trimIndent().toRequestBody(mediaType)

val request = Request.Builder()
    .url("https://api.igloohome.co/home/devices/{deviceId}/jobs/bridges/{bridgeId}")
    .method("POST", body)
    .addHeader("Authorization", "Bearer YOUR_ACCESS_TOKEN")
    .addHeader("Accept", "application/json")
    .addHeader("Content-Type", "application/json")
    .build()

val response = client.newCall(request).execute()
println(response.body?.string())
```

```swift
import Foundation

var request = URLRequest(url: URL(string: "https://api.igloohome.co/home/devices/{deviceId}/jobs/bridges/{bridgeId}")!)
request.httpMethod = "POST"
request.setValue("Bearer YOUR_ACCESS_TOKEN", forHTTPHeaderField: "Authorization")
request.setValue("application/json", forHTTPHeaderField: "Accept")
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
let payload: [String: Any] = [
    "jobData": "value",
    "jobType": "value"
]
request.httpBody = try? JSONSerialization.data(withJSONObject: payload)

let task = URLSession.shared.dataTask(with: request) { data, response, error in
    if let data = data {
        print(String(data: data, encoding: .utf8) ?? "")
    }
}
task.resume()
```

### Responses

`200 Job Created`
`400 Bad Request`
`401 Unauthorized, invalid or expired authorization token`
`402 Response`
`403 Forbidden, accessToken scope must contain the scope for the given 'jobType' (see table below)`
`404 Bridge or Device Not Found`
`406 Not Acceptable`
`415 Unsupported Media Type`
`500 Internal Server Error`

**Response body — `200`:**

- **`jobId`** `string`

**Response body — `400`:**

- **`error`** `string`

**Response body — `402`:**

- **`error`** `string`

**Response body — `406`:**

- **`error`** `string`

**Example response — `200`:**

```json
{
  "jobId": "6345191a82bcfc12a921bdb6"
}
```

**Example response — `400`:**

```json
{
  "error": "job type not supported. see documentation for supported job type(s)"
}
```

**Example response — `402`:**

```json
{
  "error": "30 days igloohome API trial has ended"
}
```

**Example response — `406`:**

```json
{
  "error": "Unable to contact bridge as it appears to be offline"
}
```

## Get Job Status

`GET` `https://api.igloohome.co/home/jobs/{jobId}`

Returns the status of a job you created earlier.

### Path Parameters

| Parameter | Type | Required |
| --- | --- | --- |
| jobId | string | Yes |

### Request Example

```bash
curl -X GET "https://api.igloohome.co/home/jobs/{jobId}" \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
  -H "Accept: application/json"
```

```go
package main

import (
	"fmt"
	"io"
	"net/http"
)

func main() {
	url := "https://api.igloohome.co/home/jobs/{jobId}"
	req, _ := http.NewRequest("GET", url, nil)
	req.Header.Set("Authorization", "Bearer YOUR_ACCESS_TOKEN")
	req.Header.Set("Accept", "application/json")

	res, _ := http.DefaultClient.Do(req)
	defer res.Body.Close()

	body, _ := io.ReadAll(res.Body)
	fmt.Println(string(body))
}
```

```javascript
const res = await fetch("https://api.igloohome.co/home/jobs/{jobId}", {
  method: "GET",
  headers: {
    "Authorization": "Bearer YOUR_ACCESS_TOKEN",
    "Accept": "application/json"
  }
});

const data = await res.json();
console.log(data);
```

```python
import requests

url = "https://api.igloohome.co/home/jobs/{jobId}"
headers = {
    "Authorization": "Bearer YOUR_ACCESS_TOKEN",
    "Accept": "application/json"
}
response = requests.get(url, headers=headers)
print(response.json())
```

```php
<?php
$curl = curl_init();

curl_setopt_array($curl, [
    CURLOPT_URL => "https://api.igloohome.co/home/jobs/{jobId}",
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_CUSTOMREQUEST => "GET",
    CURLOPT_HTTPHEADER => [
        "Authorization: Bearer YOUR_ACCESS_TOKEN",
        "Accept: application/json"
    ]
]);

$response = curl_exec($curl);
curl_close($curl);

echo $response;
```

```kotlin
val client = OkHttpClient()

val request = Request.Builder()
    .url("https://api.igloohome.co/home/jobs/{jobId}")
    .method("GET", null)
    .addHeader("Authorization", "Bearer YOUR_ACCESS_TOKEN")
    .addHeader("Accept", "application/json")
    .build()

val response = client.newCall(request).execute()
println(response.body?.string())
```

```swift
import Foundation

var request = URLRequest(url: URL(string: "https://api.igloohome.co/home/jobs/{jobId}")!)
request.httpMethod = "GET"
request.setValue("Bearer YOUR_ACCESS_TOKEN", forHTTPHeaderField: "Authorization")
request.setValue("application/json", forHTTPHeaderField: "Accept")

let task = URLSession.shared.dataTask(with: request) { data, response, error in
    if let data = data {
        print(String(data: data, encoding: .utf8) ?? "")
    }
}
task.resume()
```

### Responses

`200 OK`
`401 Unauthorized, invalid or expired authorization token`
`402 Response`
`403 Forbidden, accessToken scope must contain 'igloohomeapi/get-job-status'`
`404 Not Found`
`422 'jobId' is not a valid job ID (24-character hex)`
`500 Internal Server Error`

**Response body — `200`:**

- **`jobId`** `string`
- **`expiryDate`** `string`
- **`completed`** `boolean`
- **`jobType`** `string`
- **`jobResponse`** `object`
  - **`jobStatus`** `integer`\
    0 = completed, 1 = pending, 2 = expired
    - Allowed values: `0`, `1`, `2`
  - **`opResult`** `object`
    - **`result`** `integer`

**Response body — `402`:**

- **`error`** `string`

**Example response — `200`:**

```json
{
  "completed": true,
  "expiryDate": "2025-01-01T00:02:00.000Z",
  "jobId": "6345191a82bcfc12a921bdb6",
  "jobResponse": {
    "jobStatus": 0,
    "opResult": {
      "result": 0
    }
  },
  "jobType": "UNLOCK"
}
```

**Example response — `402`:**

```json
{
  "error": "30 days igloohome API trial has ended"
}
```

