

# Jobs API Reference

API endpoints for managing jobs.

## Bridge Proxied Jobs

### Lock Jobs

Bridge Proxied Jobs send commands to smart locks through a Bridge device. Your application calls the API; the Bridge talks to the lock over Bluetooth.

Send these jobs with [Create Bridge Proxied Job](#create-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](#job-types)) |
| jobData   | object | Command-specific parameters                          |
| departmentId | string | Required unless the account has a single department |

##### 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.

**Parameters**


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

**Request Example**

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

##### Unlock

Unlocks the device and optionally synchronizes its internal clock.

**Parameters**


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

**Request Example**

```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.

**Parameters**


| 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  |

**Request Example**

```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"
  }
}
```

**Usage Examples**

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"
  }
}
```

Permanent PIN:

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

Duration PIN (specific time window):

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

##### Delete PIN code

Removes a PIN access from the lock. Identify it by `accessId`, the ID of the PIN access record from [List Device Access](/works/api_access#list-device-access).

> **Note**: An unactivated AlgoPIN with a start date more than 256 hours after its creation cannot be deleted.

**Parameters**


| Parameter | Type   | Description            | Required |
| ----------- | -------- | ------------------------ | ---------- |
| accessId  | string | ID of the PIN access record to delete | Yes      |

**Request Example**

```json
{
  "jobType": 5,
  "jobData": {
    "accessId": "665f1c2a9b3e4d5f6a7b8c9d"
  }
}
```

##### Get Battery Level

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

**Request Example**

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

##### Get Device Status

Queries the lock's current operational status.

**Request Example**

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

##### Get Activity Logs

Downloads activity logs from the lock. Optionally synchronizes the lock's clock.

> **Important**: Activity logs are deleted from the lock after retrieval.

**Parameters**


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

**Request Example**

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

#### Error Status Codes

| Status Code | Description | Applies To |
| --- | --- | --- |
| 400 | Request validation failed (missing or invalid field) | All |
| 401 | Missing, invalid or expired access token | All |
| 402 | Account suspended, or the iglooworks API trial has ended | All |
| 403 | The token lacks the scope for this job type, or the user revoked access | All |
| 404 | No PIN access matches `jobData.accessId` | Delete PIN code |
| 406 | A pending job for the same PIN exists, or the PIN is already in use on this lock | Create Custom PIN code |
| 409 | The lock is not associated with the given department | All |
| 415 | Missing `Content-Type: application/json` | All |

### Get Bridge Job Status

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

#### Path Parameters

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

#### Request Example

```bash
curl -X GET "https://api.igloohome.co/works/bridge/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/works/bridge/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/works/bridge/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/works/bridge/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/works/bridge/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/works/bridge/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/works/bridge/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-account-suspended Payment Required. Account is billing-suspended or past its trial grace period.`
`402-trial-ended Payment Required. iglooworks API trial has ended.`
`403 Forbidden. One of: (1) the token is valid but lacks the required scope for this endpoint (no body); (2) the user revoked the OAuth client's access ('{error: "The user has revoked access for this application..."}').`
`404 Job not found.`
`422 jobId is not a valid ID.`

**Response body — `200`:**

- **`jobId`** `string`
- **`expiryDate`** `string<date-time>`
- **`completed`** `boolean`
- **`jobType`** `string`
- **`jobResponse`** `object`
  - **`jobStatus`** `integer`\
    0=complete, 1=pending, 2=expired.
  - **`opResult`** `string`\
    Absent while the job is pending. Present once a result is recorded.

**Response body — `402-account-suspended`:**

- **`error`** `string`

**Response body — `402-trial-ended`:**

- **`error`** `string`

**Response body — `403`:**

- **`error`** `string`

**Example response — `200`:**

```json
{
  "completed": true,
  "expiryDate": "2026-09-13T16:32:45.123Z",
  "jobId": "885f3d4a0d5f6e7a8b9c0d1e",
  "jobResponse": {
    "jobStatus": 0,
    "opResult": 0
  },
  "jobType": "CREATE_CUSTOM_PIN"
}
```

**Example response — `402-account-suspended`:**

```json
{
  "error": "account.payment.is.suspended"
}
```

**Example response — `402-trial-ended`:**

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

**Example response — `403`:**

```json
{
  "error": "string"
}
```

### Create Bridge Proxied Job

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

Sends a command to a lock through its Bridge: lock, unlock, create or delete a PIN, get status, or get activity logs. The shape of `jobData` depends on `jobType`. CREATE_CUSTOM_PIN and DELETE_PIN_CODE have their own sub-schemas; other job types accept an open object.

#### Path Parameters

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

#### Body Parameters

Content-Type: `application/json`

- **`jobType`** `integer` *required*\
  1=LOCK, 2=UNLOCK, 4=CREATE_CUSTOM_PIN, 5=DELETE_PIN_CODE, 9=GET_BATTERY_LEVEL, 10=GET_DEVICE_STATUS, 15=GET_ACTIVITY_LOGS.
  - Allowed values: `1`, `2`, `4`, `5`, `9`, `10`, `15`
- **`jobData`** `object`\
  Shape depends on jobType. See the operation summary.
- **`departmentId`** `string`\
  Required unless the account has a single department, in which case the API ignores it.

#### Request Example

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

```go
package main

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

func main() {
	url := "https://api.igloohome.co/works/devices/{deviceId}/jobs/bridges/{bridgeId}"
	payload := []byte(`{
		"departmentId": "value",
		"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/works/devices/{deviceId}/jobs/bridges/{bridgeId}", {
  method: "POST",
  headers: {
    "Authorization": "Bearer YOUR_ACCESS_TOKEN",
    "Accept": "application/json",
    "Content-Type": "application/json"
  },
  body: JSON.stringify({
    "departmentId": "value",
    "jobData": "value",
    "jobType": "value"
  })
});

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

```python
import requests

url = "https://api.igloohome.co/works/devices/{deviceId}/jobs/bridges/{bridgeId}"
headers = {
    "Authorization": "Bearer YOUR_ACCESS_TOKEN",
    "Accept": "application/json"
}
payload = {
    "departmentId": "value",
    "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/works/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([
        "departmentId" => "value",
        "jobData" => "value",
        "jobType" => "value"
    ])
]);

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

echo $response;
```

```kotlin
val client = OkHttpClient()

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

val request = Request.Builder()
    .url("https://api.igloohome.co/works/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/works/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] = [
    "departmentId": "value",
    "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 OK.`
`400 Query/body validation failure.`
`401 Unauthorized, invalid or expired authorization token`
`402-account-suspended Payment Required. Account is billing-suspended or past its trial grace period.`
`402-trial-ended Payment Required. iglooworks API trial has ended.`
`403 Forbidden. One of: (1) the token is valid but lacks the required scope for this endpoint (no body); (2) the user revoked the OAuth client's access ('{error: "The user has revoked access for this application..."}').`
`404 'jobType=5' (DELETE_PIN_CODE) only. No matching PIN access found for 'jobData.accessId'.`
`406 'jobType=4' (CREATE_CUSTOM_PIN) only. A pending job for the same PIN already exists, or the PIN is already in use on this lock.`
`409 The lock is not associated with the given department (or the account's only department, for a single-department account).`
`415 Missing 'Content-Type: application/json'.`

**Response body — `200`:**

- **`jobId`** `string`

**Response body — `400`:**

- **`error`** `string`

**Response body — `402-account-suspended`:**

- **`error`** `string`

**Response body — `402-trial-ended`:**

- **`error`** `string`

**Response body — `403`:**

- **`error`** `string`

**Response body — `404`:**

- **`error`** `string`

**Response body — `406`:**

- **`error`** `string`

**Response body — `409`:**

- **`error`** `string`

**Example response — `200`:**

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

**Example response — `400`:**

```json
{
  "error": "string"
}
```

**Example response — `402-account-suspended`:**

```json
{
  "error": "account.payment.is.suspended"
}
```

**Example response — `402-trial-ended`:**

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

**Example response — `403`:**

```json
{
  "error": "string"
}
```

**Example response — `404`:**

```json
{
  "error": "string"
}
```

**Example response — `406`:**

```json
{
  "error": "string"
}
```

**Example response — `409`:**

```json
{
  "error": "string"
}
```

## App Sync Jobs

### Delete PIN Access

`DELETE` `https://api.igloohome.co/works/devices/{id}/access/{accessId}`

Revokes a PIN access by creating an asynchronous delete job.

#### Path Parameters

| Parameter | Type | Required |
| --- | --- | --- |
| id | string | Yes |
| accessId | string | Yes |

#### Request Example

```bash
curl -X DELETE "https://api.igloohome.co/works/devices/{id}/access/{accessId}" \
  -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/works/devices/{id}/access/{accessId}"
	req, _ := http.NewRequest("DELETE", 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/works/devices/{id}/access/{accessId}", {
  method: "DELETE",
  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/works/devices/{id}/access/{accessId}"
headers = {
    "Authorization": "Bearer YOUR_ACCESS_TOKEN",
    "Accept": "application/json"
}
response = requests.delete(url, headers=headers)
print(response.json())
```

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

curl_setopt_array($curl, [
    CURLOPT_URL => "https://api.igloohome.co/works/devices/{id}/access/{accessId}",
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_CUSTOMREQUEST => "DELETE",
    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/works/devices/{id}/access/{accessId}")
    .method("DELETE", 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/works/devices/{id}/access/{accessId}")!)
request.httpMethod = "DELETE"
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 Delete job created.`
`401 Unauthorized, invalid or expired authorization token`
`402-account-suspended Payment Required. Account is billing-suspended or past its trial grace period.`
`402-trial-ended Payment Required. iglooworks API trial has ended.`
`403 Forbidden. One of: (1) the token is valid but lacks the required scope for this endpoint (no body); (2) the user revoked the OAuth client's access ('{error: "The user has revoked access for this application..."}').`
`404 Access not found ('{error: "access not found"}').`
`409 Response`
`415 Missing 'Content-Type: application/json'. The API enforces this header even though this DELETE has no meaningful body fields.`

**Response body — `200`:**

- **`jobId`** `string`

**Response body — `402-account-suspended`:**

- **`error`** `string`

**Response body — `402-trial-ended`:**

- **`error`** `string`

**Response body — `403`:**

- **`error`** `string`

**Response body — `404`:**

- **`error`** `string`

**Response body — `409`:**

- **`error`** `string`

**Example response — `200`:**

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

**Example response — `402-account-suspended`:**

```json
{
  "error": "account.payment.is.suspended"
}
```

**Example response — `402-trial-ended`:**

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

**Example response — `403`:**

```json
{
  "error": "string"
}
```

**Example response — `404`:**

```json
{
  "error": "string"
}
```

**Example response — `409`:**

```json
{
  "error": "string"
}
```

### List Device Jobs

`GET` `https://api.igloohome.co/works/devices/{id}/jobs`

#### Path Parameters

| Parameter | Type | Required |
| --- | --- | --- |
| id | string | Yes |

#### Query Parameters

- **`jobType`** `string`\
  Comma-separated, from: delete_pin, create_bluetooth_pin, add_group_key, delete_group_key.
- **`status`** `string`
  - Allowed values: `pending`, `complete`, `failed`
- **`departmentId`** `string`\
  Filter jobs to a single department (not comma-separated, unlike jobType).
- **`limit`** `integer`
  - Constraints: `>= 1`, `<= 300`
  - Default: `100`
- **`cursor`** `string`
- **`sort`** `string`
  - Default: `desc`
  - Allowed values: `asc`, `desc`

#### Request Example

```bash
curl -X GET "https://api.igloohome.co/works/devices/{id}/jobs?jobType=value&status=value&departmentId=value&limit=value&cursor=value&sort=value" \
  -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/works/devices/{id}/jobs?jobType=value&status=value&departmentId=value&limit=value&cursor=value&sort=value"
	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/works/devices/{id}/jobs?jobType=value&status=value&departmentId=value&limit=value&cursor=value&sort=value", {
  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/works/devices/{id}/jobs"
headers = {
    "Authorization": "Bearer YOUR_ACCESS_TOKEN",
    "Accept": "application/json"
}
params = {
    "jobType": "value",
    "status": "value",
    "departmentId": "value",
    "limit": "value",
    "cursor": "value",
    "sort": "value"
}
response = requests.get(url, headers=headers, params=params)
print(response.json())
```

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

curl_setopt_array($curl, [
    CURLOPT_URL => "https://api.igloohome.co/works/devices/{id}/jobs?jobType=value&status=value&departmentId=value&limit=value&cursor=value&sort=value",
    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/works/devices/{id}/jobs?jobType=value&status=value&departmentId=value&limit=value&cursor=value&sort=value")
    .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/works/devices/{id}/jobs?jobType=value&status=value&departmentId=value&limit=value&cursor=value&sort=value")!)
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.`
`400 Query/body validation failure.`
`401 Unauthorized, invalid or expired authorization token`
`402-account-suspended Payment Required. Account is billing-suspended or past its trial grace period.`
`402-trial-ended Payment Required. iglooworks API trial has ended.`
`403 Forbidden. One of: (1) the token is valid but lacks the required scope for this endpoint (no body); (2) the user revoked the OAuth client's access ('{error: "The user has revoked access for this application..."}').`

**Response body — `200`:**

- **`nextCursor`** `string`\
  Present only if another page exists.
- **`payload`** `array of object`
  - **`_id`** `string`
  - **`description`** `string`
  - **`status`** `string`
    - Allowed values: `pending`, `complete`, `failed`
  - **`accessData`** `object`
  - **`groupKeyData`** `object`
  - **`createdAt`** `string<date-time>`

**Response body — `400`:**

- **`error`** `string`

**Response body — `402-account-suspended`:**

- **`error`** `string`

**Response body — `402-trial-ended`:**

- **`error`** `string`

**Response body — `403`:**

- **`error`** `string`

**Example response — `200`:**

```json
{
  "nextCursor": "string",
  "payload": [
    {
      "_id": "665f1c2a9b3e4d5f6a7b8c9d",
      "accessData": {
        "accessType": "pin",
        "customPin": "5837",
        "endDateTime": "2026-09-20T18:00:00Z",
        "pinType": "duration",
        "startDateTime": "2026-09-15T10:00:00Z"
      },
      "createdAt": "2026-09-13T14:32:45.123Z",
      "description": "create_bluetooth_pin",
      "status": "pending"
    }
  ]
}
```

**Example response — `400`:**

```json
{
  "error": "string"
}
```

**Example response — `402-account-suspended`:**

```json
{
  "error": "account.payment.is.suspended"
}
```

**Example response — `402-trial-ended`:**

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

**Example response — `403`:**

```json
{
  "error": "string"
}
```

### Create Custom PIN

`POST` `https://api.igloohome.co/works/devices/{id}/jobs`

Creates an asynchronous job that sets a custom PIN on the lock over Bluetooth.

#### Path Parameters

| Parameter | Type | Required |
| --- | --- | --- |
| id | string | Yes |

#### Body Parameters

Content-Type: `application/json`

- **`description`** `string`
- **`departmentId`** `string`\
  Required unless the account has a single department, in which case the API ignores it.
- **`pinType`** `string` *required*
  - Allowed values: `duration`, `otp`, `permanent`
- **`startDateTime`** `string<date-time>` *required*\
  Format `YYYY-MM-DDTHH:mm:ss+hh:mm`. At most 1 hour in the past.
- **`endDateTime`** `string<date-time>`\
  Format `YYYY-MM-DDTHH:mm:ss+hh:mm`. Required only when pinType is duration (must be after startDateTime, at most 1 year later). Forbidden otherwise.
- **`customPin`** `string` *required*\
  4-6 digits, sent as a string.

#### Request Example

```bash
curl -X POST "https://api.igloohome.co/works/devices/{id}/jobs" \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
  -H "Accept: application/json" \
  -H "Content-Type: application/json" \
  -d '{
    "customPin": "value",
    "departmentId": "value",
    "description": "value",
    "endDateTime": "value",
    "pinType": "value",
    "startDateTime": "value"
  }'
```

```go
package main

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

func main() {
	url := "https://api.igloohome.co/works/devices/{id}/jobs"
	payload := []byte(`{
		"customPin": "value",
		"departmentId": "value",
		"description": "value",
		"endDateTime": "value",
		"pinType": "value",
		"startDateTime": "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/works/devices/{id}/jobs", {
  method: "POST",
  headers: {
    "Authorization": "Bearer YOUR_ACCESS_TOKEN",
    "Accept": "application/json",
    "Content-Type": "application/json"
  },
  body: JSON.stringify({
    "customPin": "value",
    "departmentId": "value",
    "description": "value",
    "endDateTime": "value",
    "pinType": "value",
    "startDateTime": "value"
  })
});

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

```python
import requests

url = "https://api.igloohome.co/works/devices/{id}/jobs"
headers = {
    "Authorization": "Bearer YOUR_ACCESS_TOKEN",
    "Accept": "application/json"
}
payload = {
    "customPin": "value",
    "departmentId": "value",
    "description": "value",
    "endDateTime": "value",
    "pinType": "value",
    "startDateTime": "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/works/devices/{id}/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([
        "customPin" => "value",
        "departmentId" => "value",
        "description" => "value",
        "endDateTime" => "value",
        "pinType" => "value",
        "startDateTime" => "value"
    ])
]);

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

echo $response;
```

```kotlin
val client = OkHttpClient()

val mediaType = "application/json".toMediaType()
val body = """
    {
        "customPin": "value",
        "departmentId": "value",
        "description": "value",
        "endDateTime": "value",
        "pinType": "value",
        "startDateTime": "value"
    }
""".trimIndent().toRequestBody(mediaType)

val request = Request.Builder()
    .url("https://api.igloohome.co/works/devices/{id}/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/works/devices/{id}/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] = [
    "customPin": "value",
    "departmentId": "value",
    "description": "value",
    "endDateTime": "value",
    "pinType": "value",
    "startDateTime": "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-account-suspended Payment Required. Account is billing-suspended or past its trial grace period.`
`402-trial-ended Payment Required. iglooworks API trial has ended.`
`403 Forbidden. One of: (1) the token is valid but lacks the required scope for this endpoint (no body); (2) the user revoked the OAuth client's access ('{error: "The user has revoked access for this application..."}').`
`409 Response`
`415 Missing 'Content-Type: application/json'.`

**Response body — `200`:**

- **`payload`** `object`
  - **`_id`** `string`
  - **`description`** `string`
  - **`status`** `string`
    - Allowed values: `pending`, `complete`, `failed`
  - **`accessData`** `object`
    - **`accessType`** `string`
    - **`pinType`** `string`
    - **`startDateTime`** `string<date-time>`
    - **`endDateTime`** `string<date-time>`
    - **`description`** `string`
    - **`customPin`** `string`
- **`createdAt`** `string<date-time>`

**Response body — `402-account-suspended`:**

- **`error`** `string`

**Response body — `402-trial-ended`:**

- **`error`** `string`

**Response body — `403`:**

- **`error`** `string`

**Response body — `409`:**

- **`msg`** `string`
- **`code`** `integer`

**Example response — `200`:**

```json
{
  "createdAt": "string",
  "payload": {
    "_id": "string",
    "accessData": {
      "accessType": "string",
      "customPin": "string",
      "description": "string",
      "endDateTime": "string",
      "pinType": "string",
      "startDateTime": "string"
    },
    "description": "string",
    "status": "string"
  }
}
```

**Example response — `402-account-suspended`:**

```json
{
  "error": "account.payment.is.suspended"
}
```

**Example response — `402-trial-ended`:**

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

**Example response — `403`:**

```json
{
  "error": "string"
}
```

**Example response — `409`:**

```json
{
  "code": 0,
  "msg": "string"
}
```

### Get Job

`GET` `https://api.igloohome.co/works/devices/{id}/jobs/{jobId}`

#### Path Parameters

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

#### Request Example

```bash
curl -X GET "https://api.igloohome.co/works/devices/{id}/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/works/devices/{id}/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/works/devices/{id}/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/works/devices/{id}/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/works/devices/{id}/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/works/devices/{id}/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/works/devices/{id}/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-account-suspended Payment Required. Account is billing-suspended or past its trial grace period.`
`402-trial-ended Payment Required. iglooworks API trial has ended.`
`403 Forbidden. One of: (1) the token is valid but lacks the required scope for this endpoint (no body); (2) the user revoked the OAuth client's access ('{error: "The user has revoked access for this application..."}').`
`404 Job not found.`

**Response body — `200`:**

- **`payload`** `object`
  - Example: `{"_id":"665f1c2a9b3e4d5f6a7b8c9d","accessData":{"accessType":"pin","customPin":"5837","endDateTime":"2026-09-20T18:00:00Z","pinType":"duration","startDateTime":"2026-09-15T10:00:00Z"},"createdAt":"2026-09-13T14:32:45.123Z","description":"create_bluetooth_pin","status":"pending"}`
  - **`_id`** `string`
  - **`description`** `string`
  - **`status`** `string`
    - Allowed values: `pending`, `complete`, `failed`
  - **`accessData`** `object`
  - **`groupKeyData`** `object`
  - **`createdAt`** `string<date-time>`

**Response body — `402-account-suspended`:**

- **`error`** `string`

**Response body — `402-trial-ended`:**

- **`error`** `string`

**Response body — `403`:**

- **`error`** `string`

**Response body — `404`:**

- **`error`** `string`

**Example response — `200`:**

```json
{
  "payload": {
    "_id": "665f1c2a9b3e4d5f6a7b8c9d",
    "accessData": {
      "accessType": "pin",
      "customPin": "5837",
      "endDateTime": "2026-09-20T18:00:00Z",
      "pinType": "duration",
      "startDateTime": "2026-09-15T10:00:00Z"
    },
    "createdAt": "2026-09-13T14:32:45.123Z",
    "description": "create_bluetooth_pin",
    "status": "pending"
  }
}
```

**Example response — `402-account-suspended`:**

```json
{
  "error": "account.payment.is.suspended"
}
```

**Example response — `402-trial-ended`:**

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

**Example response — `403`:**

```json
{
  "error": "string"
}
```

**Example response — `404`:**

```json
{
  "error": "string"
}
```

### Update Job Status

`PATCH` `https://api.igloohome.co/works/devices/{id}/jobs/{jobId}`

#### Path Parameters

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

#### Body Parameters

Content-Type: `application/json`

- **`status`** `string` *required*
  - Allowed values: `complete`, `failed`
- **`reason`** `string`\
  Stored only when status is failed.

#### Request Example

```bash
curl -X PATCH "https://api.igloohome.co/works/devices/{id}/jobs/{jobId}" \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
  -H "Accept: application/json" \
  -H "Content-Type: application/json" \
  -d '{
    "reason": "value",
    "status": "value"
  }'
```

```go
package main

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

func main() {
	url := "https://api.igloohome.co/works/devices/{id}/jobs/{jobId}"
	payload := []byte(`{
		"reason": "value",
		"status": "value"
	}`)

	req, _ := http.NewRequest("PATCH", 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/works/devices/{id}/jobs/{jobId}", {
  method: "PATCH",
  headers: {
    "Authorization": "Bearer YOUR_ACCESS_TOKEN",
    "Accept": "application/json",
    "Content-Type": "application/json"
  },
  body: JSON.stringify({
    "reason": "value",
    "status": "value"
  })
});

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

```python
import requests

url = "https://api.igloohome.co/works/devices/{id}/jobs/{jobId}"
headers = {
    "Authorization": "Bearer YOUR_ACCESS_TOKEN",
    "Accept": "application/json"
}
payload = {
    "reason": "value",
    "status": "value"
}
response = requests.patch(url, headers=headers, json=payload)
print(response.json())
```

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

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

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

echo $response;
```

```kotlin
val client = OkHttpClient()

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

val request = Request.Builder()
    .url("https://api.igloohome.co/works/devices/{id}/jobs/{jobId}")
    .method("PATCH", 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/works/devices/{id}/jobs/{jobId}")!)
request.httpMethod = "PATCH"
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] = [
    "reason": "value",
    "status": "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. '{payload: null}' if the job no longer exists at update time.`
`400 Query/body validation failure.`
`401 Unauthorized, invalid or expired authorization token`
`402-account-suspended Payment Required. Account is billing-suspended or past its trial grace period.`
`402-trial-ended Payment Required. iglooworks API trial has ended.`
`403 Forbidden. One of: (1) the token is valid but lacks the required scope for this endpoint (no body); (2) the user revoked the OAuth client's access ('{error: "The user has revoked access for this application..."}').`
`404 Job not found.`

**Response body — `200`:**

- **`payload`** `array`
  - **`_id`** `string`
  - **`status`** `string`
  - **`accessData`** `object`
  - **`groupKeyData`** `object`
  - **`updatedAt`** `string<date-time>`

**Response body — `400`:**

- **`error`** `string`

**Response body — `402-account-suspended`:**

- **`error`** `string`

**Response body — `402-trial-ended`:**

- **`error`** `string`

**Response body — `403`:**

- **`error`** `string`

**Response body — `404`:**

- **`error`** `string`

**Example response — `200`:**

```json
{
  "payload": null
}
```

**Example response — `400`:**

```json
{
  "error": "string"
}
```

**Example response — `402-account-suspended`:**

```json
{
  "error": "account.payment.is.suspended"
}
```

**Example response — `402-trial-ended`:**

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

**Example response — `403`:**

```json
{
  "error": "string"
}
```

**Example response — `404`:**

```json
{
  "error": "string"
}
```

