---
title: Assets Guide
description: 'Look up the assets installed at a property, their warranty coverage, and export them in bulk'
sidebar:
  label: Guide
---

The Assets API returns the assets Sibi has on record for a property: what the product is, where
it's installed, its dimensions, and its warranty coverage.

## Benefits

- **Product identity:** SKU, manufacturer, category, and serial number for each installed asset
- **Warranty visibility:** Time-sliced coverage windows so you can tell what's covered and when
- **Address-scoped:** Narrow results to the assets installed at one property
- **Single reads:** Fetch one asset by its id once you hold it
- **Bulk export:** Request a file of all your assets, then download it

## Core Concepts

**Asset**

A single product installed at a property, with its identity, dimensions, and warranty coverage

**Warranty Coverage**

A time-sliced window during which a specific set of coverage types applies to an asset

**Export**

A background job that writes all your assets into one downloadable file

## Asset ids

Every asset id this API returns carries the `as_` prefix, so an id is self-describing wherever it is logged or stored.
`GET /assets/{id}` accepts the id either way: as returned here (`as_aBcD1234`) or bare (`aBcD1234`), which is the
form GraphQL responses and version `1` [asset events](/events/assets#id) carry.

## Pagination

Results are paged with an opaque cursor. Pass the previous response's `next` back as `after` to
fetch the next page; `next` is `null` once a page comes back short.

> **Warning**
>
> Calling without an `address` browses every asset the caller can see across all properties. This
> is meant for one-off backfills, not realtime use, and can be slow at scale — supply `address` to
> narrow to one property instead.

## Exports

An export writes all your assets into one file, so you can load your whole portfolio without
paging through `GET /assets`. It includes the assets in every office the token's user can access.
Exports run in the background: you create one, poll it until it is ready, then download the file.

1. **Create the export**

    `POST /assets/exports` with a token that carries `create:asset-exports`. There is no request body.

2. **Poll until it is ready**

    `GET /assets/exports/{id}` with `read:asset-exports` until `status` is `succeeded` or `failed`.

3. **Download the file**

    Fetch `download.url`. The file is gzipped JSON Lines.

### Create the export

```shell Curl
curl \
  --request POST \
  --header 'Authorization: Bearer <YOUR_API_KEY>' \
  https://api.sibipro.com/assets/exports
```

```javascript Fetch
const response = await fetch('https://api.sibipro.com/assets/exports', {
  method: 'POST',
  headers: { Authorization: `Bearer ${yourApiKey}` },
});
const exportJob = await response.json();
```

A successful call returns `202 Accepted` with the export in `in_progress` status and a `Location` header
pointing at it:

```json Response
{
  "id": "ae_aBcD1234",
  "status": "in_progress",
  "createdAt": "2026-09-24T18:00:00.000Z"
}
```

> **One export at a time**
>
> Only one export can be in progress per organization. A second `POST` while one is `in_progress`
> returns `409` with code `EXPORT_IN_PROGRESS` and the running export's id in
> `exportId`. Poll that export instead; once it has succeeded or failed, you can create a new one.

### Poll for the result

```shell Curl
curl \
  --header 'Authorization: Bearer <YOUR_API_KEY>' \
  https://api.sibipro.com/assets/exports/ae_aBcD1234
```

An export stays `in_progress` until it moves to `succeeded` or `failed`. Poll every few seconds;
a large portfolio can take several minutes. Once it has succeeded, the response carries the file's
row count, size, and a download link:

```json Response
{
  "id": "ae_aBcD1234",
  "status": "succeeded",
  "createdAt": "2026-09-24T18:00:00.000Z",
  "startedAt": "2026-09-24T18:00:02.000Z",
  "completedAt": "2026-09-24T18:03:40.000Z",
  "rowCount": 48210,
  "fileSize": 6291456,
  "download": {
    "url": "https://…",
    "method": "GET",
    "expiresAt": "2026-09-24T18:19:12.000Z"
  }
}
```

A `failed` export carries an `error` with a `code` and `message` instead. A failed export could not
be generated; create a new export to try again.

### Download the file

`download.url` is a signed link to the file. The link is
valid for 15 minutes from the time you read the export; read the export again to get a fresh one.

```shell Curl
curl --output assets.jsonl.gz '<download.url>'
gunzip assets.jsonl.gz
```

The file is gzipped [JSON Lines](https://jsonlines.org/): one asset per line, in exactly the shape
`GET /assets/{id}` returns, so a single parser handles both.

The file is a snapshot of your assets as of the export's `createdAt`. A succeeded export keeps its
download link, so you can fetch the file again later; create a new export when you want current data.

## Test environment

Send requests to `https://dev.sibi.pro/assets` with a test REST API token to work with test assets. Test assets are
kept apart from your real assets: a test request reads, browses, and exports only test assets, and a production
request never sees them. An export created in one environment returns `404` in the other.

> **Test assets last 7 days**
>
> An asset created or imported in the test environment is deleted 7 days after it is created. Updating it does not
> extend that. After it is deleted it no longer appears in `GET /assets`, `GET /assets/{id}`, or new exports, so do
> not rely on a test asset for longer than 7 days; create or import it again when you need it.

See [Test REST Environment](/rest/test-environment) for how the test environment works.

## Authentication

Every Assets endpoint requires a Public REST API token. Create the token in the
[developer dashboard](https://developer.sibipro.com), grant it the scopes below, then send it as
`Authorization: Bearer <token>`.

| Endpoint | Scope |
| --- | --- |
| `GET /assets`, `GET /assets/{id}` | `read:assets` |
| `POST /assets/exports` | `create:asset-exports` |
| `GET /assets/exports/{id}` | `read:asset-exports` |

A request with a token that lacks the scope receives a `403` with code `INSUFFICIENT_SCOPE`; a token
issued for the GraphQL API receives a `403` with code `REST_TOKEN_REQUIRED`.

## Next Steps

1. **Review Documentation**

    Read through API reference and authentication requirements

2. **Development**

    Try listing assets for a property by address

3. **Production**

    Go live with your integration

## Endpoints

**[Assets endpoints](/api-reference/assets)**

Every endpoint with parameters, response schemas, and code samples.
