

# Access API Reference

API endpoints for managing access.

## AlgoPIN

### Create a Daily AlgoPIN

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

See `POST /devices/{id}/algopin/permanent`. Identical shape, different scope. `endDate` is required for this PIN type (forbidden for permanent/onetime), and `variance` range is 1–3 (not 1–10).

#### Path Parameters

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

#### Body Parameters

Content-Type: `application/json`

- **`accessName`** `string` *required*
- **`departmentId`** `string`\
  Required unless the account has a single department.
- **`startDate`** `string` *required*\
  Format `YYYY-MM-DDTHH:00:00+hh:mm`. Must be now or after.
- **`endDate`** `string`\
  Format `YYYY-MM-DDTHH:00:00+hh:mm`. Required for daily/hourly, forbidden for onetime/permanent. Must be after `startDate`. Hourly: at most 672 hours after `startDate`.
- **`variance`** `integer` *required*\
  Daily/hourly: 1–3. Onetime/permanent: 1–10. Values 6–10 require newer lock firmware (some older locks support them after a firmware update); otherwise use 1–5.

#### Request Example

```bash
curl -X POST "https://api.igloohome.co/works/devices/{id}/algopin/daily" \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
  -H "Accept: application/json" \
  -H "Content-Type: application/json" \
  -d '{
    "accessName": "value",
    "departmentId": "value",
    "endDate": "value",
    "startDate": "value",
    "variance": "value"
  }'
```

```go
package main

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

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

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

```python
import requests

url = "https://api.igloohome.co/works/devices/{id}/algopin/daily"
headers = {
    "Authorization": "Bearer YOUR_ACCESS_TOKEN",
    "Accept": "application/json"
}
payload = {
    "accessName": "value",
    "departmentId": "value",
    "endDate": "value",
    "startDate": "value",
    "variance": "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}/algopin/daily",
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_CUSTOMREQUEST => "POST",
    CURLOPT_HTTPHEADER => [
        "Authorization: Bearer YOUR_ACCESS_TOKEN",
        "Accept: application/json",
        "Content-Type: application/json"
    ],
    CURLOPT_POSTFIELDS => json_encode([
        "accessName" => "value",
        "departmentId" => "value",
        "endDate" => "value",
        "startDate" => "value",
        "variance" => "value"
    ])
]);

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

echo $response;
```

```kotlin
val client = OkHttpClient()

val mediaType = "application/json".toMediaType()
val body = """
    {
        "accessName": "value",
        "departmentId": "value",
        "endDate": "value",
        "startDate": "value",
        "variance": "value"
    }
""".trimIndent().toRequestBody(mediaType)

val request = Request.Builder()
    .url("https://api.igloohome.co/works/devices/{id}/algopin/daily")
    .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}/algopin/daily")!)
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] = [
    "accessName": "value",
    "departmentId": "value",
    "endDate": "value",
    "startDate": "value",
    "variance": "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. If a matching PIN already exists for this lock, type, and window with the same 'variance', the API returns the same success response without creating a new PIN. This idempotent behavior is not an error.`
`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 Missing a required scope: AlgoPIN endpoints need both the general AlgoPIN scope and the scope for the specific PIN type (see the endpoint description).`
`409 Response`
`415 Missing 'Content-Type: application/json'.`

**Response body — `200`:**

- **`pin`** `string`
- **`pinId`** `string`

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

- **`error`** `string`

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

- **`error`** `string`

**Example response — `200`:**

```json
{
  "pin": "string",
  "pinId": "string"
}
```

**Example response — `400`:**

```json
{
  "error": "departmentId is missing"
}
```

**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 — `409`:**

```json
{
  "error": "department not found"
}
```

### Create an Hourly AlgoPIN

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

See `POST /devices/{id}/algopin/permanent`. Identical shape, different scope. `endDate` is required for this PIN type (forbidden for permanent/onetime), and `variance` range is 1–3 (not 1–10).

#### Path Parameters

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

#### Body Parameters

Content-Type: `application/json`

- **`accessName`** `string` *required*
- **`departmentId`** `string`\
  Required unless the account has a single department.
- **`startDate`** `string` *required*\
  Format `YYYY-MM-DDTHH:00:00+hh:mm`. Must be now or after.
- **`endDate`** `string`\
  Format `YYYY-MM-DDTHH:00:00+hh:mm`. Required for daily/hourly, forbidden for onetime/permanent. Must be after `startDate`. Hourly: at most 672 hours after `startDate`.
- **`variance`** `integer` *required*\
  Daily/hourly: 1–3. Onetime/permanent: 1–10. Values 6–10 require newer lock firmware (some older locks support them after a firmware update); otherwise use 1–5.

#### Request Example

```bash
curl -X POST "https://api.igloohome.co/works/devices/{id}/algopin/hourly" \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
  -H "Accept: application/json" \
  -H "Content-Type: application/json" \
  -d '{
    "accessName": "value",
    "departmentId": "value",
    "endDate": "value",
    "startDate": "value",
    "variance": "value"
  }'
```

```go
package main

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

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

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

```python
import requests

url = "https://api.igloohome.co/works/devices/{id}/algopin/hourly"
headers = {
    "Authorization": "Bearer YOUR_ACCESS_TOKEN",
    "Accept": "application/json"
}
payload = {
    "accessName": "value",
    "departmentId": "value",
    "endDate": "value",
    "startDate": "value",
    "variance": "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}/algopin/hourly",
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_CUSTOMREQUEST => "POST",
    CURLOPT_HTTPHEADER => [
        "Authorization: Bearer YOUR_ACCESS_TOKEN",
        "Accept: application/json",
        "Content-Type: application/json"
    ],
    CURLOPT_POSTFIELDS => json_encode([
        "accessName" => "value",
        "departmentId" => "value",
        "endDate" => "value",
        "startDate" => "value",
        "variance" => "value"
    ])
]);

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

echo $response;
```

```kotlin
val client = OkHttpClient()

val mediaType = "application/json".toMediaType()
val body = """
    {
        "accessName": "value",
        "departmentId": "value",
        "endDate": "value",
        "startDate": "value",
        "variance": "value"
    }
""".trimIndent().toRequestBody(mediaType)

val request = Request.Builder()
    .url("https://api.igloohome.co/works/devices/{id}/algopin/hourly")
    .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}/algopin/hourly")!)
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] = [
    "accessName": "value",
    "departmentId": "value",
    "endDate": "value",
    "startDate": "value",
    "variance": "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. If a matching PIN already exists for this lock, type, and window with the same 'variance', the API returns the same success response without creating a new PIN. This idempotent behavior is not an error.`
`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 Missing a required scope: AlgoPIN endpoints need both the general AlgoPIN scope and the scope for the specific PIN type (see the endpoint description).`
`409 Response`
`415 Missing 'Content-Type: application/json'.`

**Response body — `200`:**

- **`pin`** `string`
- **`pinId`** `string`

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

- **`error`** `string`

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

- **`error`** `string`

**Example response — `200`:**

```json
{
  "pin": "string",
  "pinId": "string"
}
```

**Example response — `400`:**

```json
{
  "error": "departmentId is missing"
}
```

**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 — `409`:**

```json
{
  "error": "department not found"
}
```

### Create a One-Time AlgoPIN

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

See `POST /devices/{id}/algopin/permanent`. Identical shape, different scope.

#### Path Parameters

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

#### Body Parameters

Content-Type: `application/json`

- **`accessName`** `string` *required*
- **`departmentId`** `string`\
  Required unless the account has a single department.
- **`startDate`** `string` *required*\
  Format `YYYY-MM-DDTHH:00:00+hh:mm`. Must be now or after.
- **`endDate`** `string`\
  Format `YYYY-MM-DDTHH:00:00+hh:mm`. Required for daily/hourly, forbidden for onetime/permanent. Must be after `startDate`. Hourly: at most 672 hours after `startDate`.
- **`variance`** `integer` *required*\
  Daily/hourly: 1–3. Onetime/permanent: 1–10. Values 6–10 require newer lock firmware (some older locks support them after a firmware update); otherwise use 1–5.

#### Request Example

```bash
curl -X POST "https://api.igloohome.co/works/devices/{id}/algopin/onetime" \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
  -H "Accept: application/json" \
  -H "Content-Type: application/json" \
  -d '{
    "accessName": "value",
    "departmentId": "value",
    "endDate": "value",
    "startDate": "value",
    "variance": "value"
  }'
```

```go
package main

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

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

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

```python
import requests

url = "https://api.igloohome.co/works/devices/{id}/algopin/onetime"
headers = {
    "Authorization": "Bearer YOUR_ACCESS_TOKEN",
    "Accept": "application/json"
}
payload = {
    "accessName": "value",
    "departmentId": "value",
    "endDate": "value",
    "startDate": "value",
    "variance": "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}/algopin/onetime",
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_CUSTOMREQUEST => "POST",
    CURLOPT_HTTPHEADER => [
        "Authorization: Bearer YOUR_ACCESS_TOKEN",
        "Accept: application/json",
        "Content-Type: application/json"
    ],
    CURLOPT_POSTFIELDS => json_encode([
        "accessName" => "value",
        "departmentId" => "value",
        "endDate" => "value",
        "startDate" => "value",
        "variance" => "value"
    ])
]);

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

echo $response;
```

```kotlin
val client = OkHttpClient()

val mediaType = "application/json".toMediaType()
val body = """
    {
        "accessName": "value",
        "departmentId": "value",
        "endDate": "value",
        "startDate": "value",
        "variance": "value"
    }
""".trimIndent().toRequestBody(mediaType)

val request = Request.Builder()
    .url("https://api.igloohome.co/works/devices/{id}/algopin/onetime")
    .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}/algopin/onetime")!)
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] = [
    "accessName": "value",
    "departmentId": "value",
    "endDate": "value",
    "startDate": "value",
    "variance": "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. If a matching PIN already exists for this lock, type, and window with the same 'variance', the API returns the same success response without creating a new PIN. This idempotent behavior is not an error.`
`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 Missing a required scope: AlgoPIN endpoints need both the general AlgoPIN scope and the scope for the specific PIN type (see the endpoint description).`
`409 Response`
`415 Missing 'Content-Type: application/json'.`

**Response body — `200`:**

- **`pin`** `string`
- **`pinId`** `string`

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

- **`error`** `string`

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

- **`error`** `string`

**Example response — `200`:**

```json
{
  "pin": "string",
  "pinId": "string"
}
```

**Example response — `400`:**

```json
{
  "error": "departmentId is missing"
}
```

**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 — `409`:**

```json
{
  "error": "department not found"
}
```

### Create a Passage AlgoPIN

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

Allows unlock with AlgoPIN and keeps the lock unlocked for the duration given in the request body. Supports newer devices with the latest firmware only.

#### Path Parameters

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

#### Body Parameters

Content-Type: `application/json`

- **`variance`** `integer` *required*
  - Allowed values: `1`
- **`startDate`** `string<date-time>` *required*\
  Date and time when the permission starts. Must be in format `YYYY-MM-DDTHH:00:00+hh:mm` (hour resolution only). The PIN is valid from `startDate` for `durationHours` hours.
  - Example: `2022-01-01T12:00:00+08:00`
- **`durationHours`** `integer` *required*\
  Must be between 1 and 12 hours
- **`accessName`** `string` *required*\
  Name or short description of the access
  - Example: `Maintenance guy`
- **`departmentId`** `string`\
  Required unless the account has a single department.

#### Request Example

```bash
curl -X POST "https://api.igloohome.co/works/devices/{id}/algopin/passage" \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
  -H "Accept: application/json" \
  -H "Content-Type: application/json" \
  -d '{
    "accessName": "value",
    "departmentId": "value",
    "durationHours": "value",
    "startDate": "value",
    "variance": "value"
  }'
```

```go
package main

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

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

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

```python
import requests

url = "https://api.igloohome.co/works/devices/{id}/algopin/passage"
headers = {
    "Authorization": "Bearer YOUR_ACCESS_TOKEN",
    "Accept": "application/json"
}
payload = {
    "accessName": "value",
    "departmentId": "value",
    "durationHours": "value",
    "startDate": "value",
    "variance": "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}/algopin/passage",
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_CUSTOMREQUEST => "POST",
    CURLOPT_HTTPHEADER => [
        "Authorization: Bearer YOUR_ACCESS_TOKEN",
        "Accept: application/json",
        "Content-Type: application/json"
    ],
    CURLOPT_POSTFIELDS => json_encode([
        "accessName" => "value",
        "departmentId" => "value",
        "durationHours" => "value",
        "startDate" => "value",
        "variance" => "value"
    ])
]);

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

echo $response;
```

```kotlin
val client = OkHttpClient()

val mediaType = "application/json".toMediaType()
val body = """
    {
        "accessName": "value",
        "departmentId": "value",
        "durationHours": "value",
        "startDate": "value",
        "variance": "value"
    }
""".trimIndent().toRequestBody(mediaType)

val request = Request.Builder()
    .url("https://api.igloohome.co/works/devices/{id}/algopin/passage")
    .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}/algopin/passage")!)
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] = [
    "accessName": "value",
    "departmentId": "value",
    "durationHours": "value",
    "startDate": "value",
    "variance": "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. If a matching PIN already exists for this lock, type, and window with the same 'variance', the API returns the same success response without creating a new PIN. This idempotent behavior is not an error.`
`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 Missing a required scope: AlgoPIN endpoints need both the general AlgoPIN scope and the scope for the specific PIN type (see the endpoint description).`
`409 Response`
`415 Missing 'Content-Type: application/json'.`

**Response body — `200`:**

- **`pin`** `string`
- **`pinId`** `string`

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

- **`error`** `string`

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

- **`error`** `string`

**Example response — `200`:**

```json
{
  "pin": "string",
  "pinId": "string"
}
```

**Example response — `400`:**

```json
{
  "error": "departmentId is missing"
}
```

**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 — `409`:**

```json
{
  "error": "department not found"
}
```

### Create a Permanent AlgoPIN

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

Request and response shape match the other AlgoPIN endpoints (onetime/daily/hourly). Requires the `algopin-permanent` scope. A different AlgoPIN scope (e.g. `algopin-daily`) is not sufficient and returns a 403.

#### Path Parameters

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

#### Body Parameters

Content-Type: `application/json`

- **`accessName`** `string` *required*
- **`departmentId`** `string`\
  Required unless the account has a single department.
- **`startDate`** `string` *required*\
  Format `YYYY-MM-DDTHH:00:00+hh:mm`. Must be now or after.
- **`endDate`** `string`\
  Format `YYYY-MM-DDTHH:00:00+hh:mm`. Required for daily/hourly, forbidden for onetime/permanent. Must be after `startDate`. Hourly: at most 672 hours after `startDate`.
- **`variance`** `integer` *required*\
  Daily/hourly: 1–3. Onetime/permanent: 1–10. Values 6–10 require newer lock firmware (some older locks support them after a firmware update); otherwise use 1–5.

#### Request Example

```bash
curl -X POST "https://api.igloohome.co/works/devices/{id}/algopin/permanent" \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
  -H "Accept: application/json" \
  -H "Content-Type: application/json" \
  -d '{
    "accessName": "value",
    "departmentId": "value",
    "endDate": "value",
    "startDate": "value",
    "variance": "value"
  }'
```

```go
package main

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

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

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

```python
import requests

url = "https://api.igloohome.co/works/devices/{id}/algopin/permanent"
headers = {
    "Authorization": "Bearer YOUR_ACCESS_TOKEN",
    "Accept": "application/json"
}
payload = {
    "accessName": "value",
    "departmentId": "value",
    "endDate": "value",
    "startDate": "value",
    "variance": "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}/algopin/permanent",
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_CUSTOMREQUEST => "POST",
    CURLOPT_HTTPHEADER => [
        "Authorization: Bearer YOUR_ACCESS_TOKEN",
        "Accept: application/json",
        "Content-Type: application/json"
    ],
    CURLOPT_POSTFIELDS => json_encode([
        "accessName" => "value",
        "departmentId" => "value",
        "endDate" => "value",
        "startDate" => "value",
        "variance" => "value"
    ])
]);

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

echo $response;
```

```kotlin
val client = OkHttpClient()

val mediaType = "application/json".toMediaType()
val body = """
    {
        "accessName": "value",
        "departmentId": "value",
        "endDate": "value",
        "startDate": "value",
        "variance": "value"
    }
""".trimIndent().toRequestBody(mediaType)

val request = Request.Builder()
    .url("https://api.igloohome.co/works/devices/{id}/algopin/permanent")
    .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}/algopin/permanent")!)
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] = [
    "accessName": "value",
    "departmentId": "value",
    "endDate": "value",
    "startDate": "value",
    "variance": "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. If a matching PIN already exists for this lock, type, and window with the same 'variance', the API returns the same success response without creating a new PIN. This idempotent behavior is not an error.`
`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 Missing a required scope: AlgoPIN endpoints need both the general AlgoPIN scope and the scope for the specific PIN type (see the endpoint description).`
`409 Response`
`415 Missing 'Content-Type: application/json'.`

**Response body — `200`:**

- **`pin`** `string`
- **`pinId`** `string`

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

- **`error`** `string`

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

- **`error`** `string`

**Example response — `200`:**

```json
{
  "pin": "string",
  "pinId": "string"
}
```

**Example response — `400`:**

```json
{
  "error": "departmentId is missing"
}
```

**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 — `409`:**

```json
{
  "error": "department not found"
}
```

## eKey

### Grant eKey Access

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

Creates an eKey access record for the recipient and logs it in device activity and access history. All error responses on this endpoint use a `{message, code}` body shape. See the 404 response below for the specific not-found causes.

#### Path Parameters

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

#### Body Parameters

Content-Type: `application/json`

- **`recipientId`** `string` *required*
- **`startDateTime`** `string<date-time>` *required*\
  Must include an explicit timezone offset ('Z' or '+08:00').
- **`endDateTime`** `string<date-time>` *required*\
  Must be after startDateTime and after the current time. Same explicit-timezone-offset requirement.
- **`permissions`** `array of string` *required*\
  Narrower allow-list than POST /ekey's 56-value list, even though both mint an eKey.
- **`departmentRef`** `string`
- **`lang`** `string`

#### Request Example

```bash
curl -X POST "https://api.igloohome.co/works/devices/{id}/access/grant" \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
  -H "Accept: application/json" \
  -H "Content-Type: application/json" \
  -d '{
    "departmentRef": "value",
    "endDateTime": "value",
    "lang": "value",
    "permissions": "value",
    "recipientId": "value",
    "startDateTime": "value"
  }'
```

```go
package main

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

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

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

```python
import requests

url = "https://api.igloohome.co/works/devices/{id}/access/grant"
headers = {
    "Authorization": "Bearer YOUR_ACCESS_TOKEN",
    "Accept": "application/json"
}
payload = {
    "departmentRef": "value",
    "endDateTime": "value",
    "lang": "value",
    "permissions": "value",
    "recipientId": "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}/access/grant",
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_CUSTOMREQUEST => "POST",
    CURLOPT_HTTPHEADER => [
        "Authorization: Bearer YOUR_ACCESS_TOKEN",
        "Accept: application/json",
        "Content-Type: application/json"
    ],
    CURLOPT_POSTFIELDS => json_encode([
        "departmentRef" => "value",
        "endDateTime" => "value",
        "lang" => "value",
        "permissions" => "value",
        "recipientId" => "value",
        "startDateTime" => "value"
    ])
]);

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

echo $response;
```

```kotlin
val client = OkHttpClient()

val mediaType = "application/json".toMediaType()
val body = """
    {
        "departmentRef": "value",
        "endDateTime": "value",
        "lang": "value",
        "permissions": "value",
        "recipientId": "value",
        "startDateTime": "value"
    }
""".trimIndent().toRequestBody(mediaType)

val request = Request.Builder()
    .url("https://api.igloohome.co/works/devices/{id}/access/grant")
    .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}/access/grant")!)
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] = [
    "departmentRef": "value",
    "endDateTime": "value",
    "lang": "value",
    "permissions": "value",
    "recipientId": "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

`201 Created. If activity logging fails after the API saves the access record, the API still returns 201, but with a different response body (no 'code' field) containing an error message.`
`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..."}').`
`404 RECIPIENT_NOT_FOUND / LOCK_NOT_FOUND / DEPARTMENT_NOT_FOUND.`
`409 CONFIGURATION_LOCK_ISSUE / FAILED_GENERATE_EKEY.`
`415 Missing 'Content-Type: application/json'.`
`422 NON_LOCK_DEVICE_NOT_ALLOWED. Returned when the target device is not a Lock.`
`500 FLAT_ORG_DEPT_NOT_FOUND / INTERNAL_SERVER_ERROR.`

**Response body — `201`:**

- **`message`** `string`
- **`accesses`** `array of object`
  - **`_id`** `string`
  - **`userRef`** `string`
  - **`recipient`** `string`
  - **`lockRef`** `string`
  - **`accessType`** `string`
    - Allowed values: `ekey`
  - **`via`** `string`
  - **`startDateTime`** `string<date-time>`
  - **`endDateTime`** `string<date-time>`
  - **`permissions`** `array of string`
  - **`departmentRef`** `string`
  - **`description`** `string`
  - **`bluetoothGuestKey`** `string`
  - **`keyId`** `number`
  - **`accessRight`** `string`

**Response body — `400`:**

- **`message`** `string`
- **`code`** `string`

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

- **`error`** `string`

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

- **`error`** `string`

**Response body — `403`:**

- **`error`** `string`

**Response body — `404`:**

- **`message`** `string`
- **`code`** `string`

**Response body — `409`:**

- **`message`** `string`
- **`code`** `string`

**Response body — `422`:**

- **`message`** `string`
- **`code`** `string`

**Response body — `500`:**

- **`message`** `string`
- **`code`** `string`

**Example response — `201`:**

```json
{
  "accesses": [
    {
      "_id": "string",
      "accessRight": "string",
      "accessType": "string",
      "bluetoothGuestKey": "string",
      "departmentRef": "string",
      "description": "string",
      "endDateTime": "string",
      "keyId": 0,
      "lockRef": "string",
      "permissions": [
        "string"
      ],
      "recipient": "string",
      "startDateTime": "string",
      "userRef": "string",
      "via": "string"
    }
  ],
  "message": "string"
}
```

**Example response — `400`:**

```json
{
  "code": "string",
  "message": "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
{
  "code": "string",
  "message": "string"
}
```

**Example response — `409`:**

```json
{
  "code": "string",
  "message": "string"
}
```

**Example response — `422`:**

```json
{
  "code": "string",
  "message": "string"
}
```

**Example response — `500`:**

```json
{
  "code": "string",
  "message": "string"
}
```

### Revoke eKey Access

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

Revokes all matching access records for this recipient and permanently deletes their associated key material. You cannot undo this action.

#### Path Parameters

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

#### Body Parameters

Content-Type: `application/json`

- **`recipientId`** `string` *required*

#### Request Example

```bash
curl -X POST "https://api.igloohome.co/works/devices/{id}/access/revoke" \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
  -H "Accept: application/json" \
  -H "Content-Type: application/json" \
  -d '{
    "recipientId": "value"
  }'
```

```go
package main

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

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

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

```python
import requests

url = "https://api.igloohome.co/works/devices/{id}/access/revoke"
headers = {
    "Authorization": "Bearer YOUR_ACCESS_TOKEN",
    "Accept": "application/json"
}
payload = {
    "recipientId": "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}/access/revoke",
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_CUSTOMREQUEST => "POST",
    CURLOPT_HTTPHEADER => [
        "Authorization: Bearer YOUR_ACCESS_TOKEN",
        "Accept: application/json",
        "Content-Type: application/json"
    ],
    CURLOPT_POSTFIELDS => json_encode([
        "recipientId" => "value"
    ])
]);

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

echo $response;
```

```kotlin
val client = OkHttpClient()

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

val request = Request.Builder()
    .url("https://api.igloohome.co/works/devices/{id}/access/revoke")
    .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}/access/revoke")!)
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] = [
    "recipientId": "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 RECIPIENT_NOT_FOUND / LOCK_NOT_FOUND / NO_ACTIVE_ACCESS ("Ekey already revoked or not found").`
`415 Missing 'Content-Type: application/json'.`
`500 INTERNAL_SERVER_ERROR. Unlike grant, revoke has no tolerant fallback if activity/audit-trail logging fails. Any failure here returns a 500.`

**Response body — `200`:**

- **`message`** `string`
  - Allowed values: `Access revoked successfully`
- **`revokedCount`** `integer`

**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`:**

- **`message`** `string`
- **`code`** `string`

**Response body — `500`:**

- **`message`** `string`
- **`code`** `string`

**Example response — `200`:**

```json
{
  "message": "string",
  "revokedCount": 0
}
```

**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
{
  "code": "string",
  "message": "string"
}
```

**Example response — `500`:**

```json
{
  "code": "string",
  "message": "string"
}
```

### Generate eKey

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

Returns an eKey without creating an access record. To create a tracked access record, use Grant eKey Access. Error responses are plain text, not JSON. Only a successful response returns a JSON body.

#### Path Parameters

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

#### Body Parameters

Content-Type: `application/json`

- **`startDate`** `string` *required*\
  Format `YYYY-MM-DDTHH:mm:00Z` (minute resolution).
- **`endDate`** `string` *required*\
  Must be after `startDate`. Same format.
- **`permissions`** `array of string` *required*\
  Each must be a valid permission type (56 supported values).

#### Request Example

```bash
curl -X POST "https://api.igloohome.co/works/devices/{id}/ekey" \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
  -H "Accept: application/json" \
  -H "Content-Type: application/json" \
  -d '{
    "endDate": "value",
    "permissions": "value",
    "startDate": "value"
  }'
```

```go
package main

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

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

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

```python
import requests

url = "https://api.igloohome.co/works/devices/{id}/ekey"
headers = {
    "Authorization": "Bearer YOUR_ACCESS_TOKEN",
    "Accept": "application/json"
}
payload = {
    "endDate": "value",
    "permissions": "value",
    "startDate": "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}/ekey",
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_CUSTOMREQUEST => "POST",
    CURLOPT_HTTPHEADER => [
        "Authorization: Bearer YOUR_ACCESS_TOKEN",
        "Accept: application/json",
        "Content-Type: application/json"
    ],
    CURLOPT_POSTFIELDS => json_encode([
        "endDate" => "value",
        "permissions" => "value",
        "startDate" => "value"
    ])
]);

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

echo $response;
```

```kotlin
val client = OkHttpClient()

val mediaType = "application/json".toMediaType()
val body = """
    {
        "endDate": "value",
        "permissions": "value",
        "startDate": "value"
    }
""".trimIndent().toRequestBody(mediaType)

val request = Request.Builder()
    .url("https://api.igloohome.co/works/devices/{id}/ekey")
    .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}/ekey")!)
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] = [
    "endDate": "value",
    "permissions": "value",
    "startDate": "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-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 Plain-text body: '"failed.generate.ekey"' (key derivation failed).`
`415 Missing 'Content-Type: application/json'.`
`500 Plain-text body. Uncaught exception.`

**Response body — `200`:**

- **`keyId`** `number`
- **`bluetoothGuestKey`** `string`\
  Base64-encoded obfuscated blob.

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

- **`error`** `string`

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

- **`error`** `string`

**Response body — `403`:**

- **`error`** `string`

**Example response — `200`:**

```json
{
  "bluetoothGuestKey": "string",
  "keyId": 0
}
```

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

## Access Records

### List Device Access

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

A malformed query parameter returns a 400, even if authentication is missing or invalid.

#### Path Parameters

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

#### Query Parameters

- **`accessType`** `string`\
  Comma-separated list from `pin,ekey,nfc,fingerprint`. Invalid entries → 400 `{error: "valid 'accessType' - pin,ekey,nfc,fingerprint"}`.
- **`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}/access?accessType=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}/access?accessType=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}/access?accessType=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}/access"
headers = {
    "Authorization": "Bearer YOUR_ACCESS_TOKEN",
    "Accept": "application/json"
}
params = {
    "accessType": "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}/access?accessType=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}/access?accessType=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}/access?accessType=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..."}').`
`404 Device not found, or (Lock/Keypad only) Property not found.`
`409 Property timezone is missing.`

**Response body — `200`:**

- **`nextCursor`** `string`\
  Present only if another page exists.
- **`payload`** `array of object`
  - **`id`** `string`
  - **`name`** `string`
  - **`accessType`** `string`
    - Allowed values: `pin`, `ekey`, `nfc`, `fingerprint`
  - **`bluetoothDeviceName`** `string`
  - **`isCustomPin`** `boolean`\
    Present only if accessType===pin.
  - **`pin`** `string`\
    Present only if accessType===pin.
  - **`pinType`** `string`\
    Present only if accessType===pin. The API stores and reports AlgoPIN daily/hourly variants as `duration`.
    - Allowed values: `otp`, `duration`, `permanent`
  - **`startDateTime`** `string<date-time>`
  - **`endDateTime`** `string<date-time>`
  - **`recipient`** `object`\
    Present only if `accessType` is `ekey`.
    - **`name`** `string`
    - **`email`** `string`
  - **`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`

**Response body — `404`:**

- **`message`** `string`

**Response body — `409`:**

- **`message`** `string`

**Example response — `200`:**

```json
{
  "nextCursor": "string",
  "payload": [
    {
      "accessType": "pin",
      "bluetoothDeviceName": "IGH-LK-3F9A21",
      "createdAt": "2026-09-13T09:30:00+08:00",
      "endDateTime": "2026-09-14T18:00:00+08:00",
      "id": "556e1b3a8c2d4e5f6a7b8c8e",
      "isCustomPin": true,
      "name": "Temp PIN - Maintenance",
      "pin": "2847",
      "pinType": "duration",
      "startDateTime": "2026-09-13T10:00:00+08:00"
    }
  ]
}
```

**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
{
  "message": "string"
}
```

**Example response — `409`:**

```json
{
  "message": "string"
}
```

