Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,12 @@ The format is inspired by Keep a Changelog and this project follows SemVer for `

## [Unreleased]

### Changed
- `organization.devices.getHistories` catalog notes and skill docs now document `page`/`per_page` pagination (422 outside 1..1000, not clamped), the 31-day inclusive `from`/`to` window (422 when exceeded or reversed), the stable newest-first ordering, and guidance to prefer narrow windows and filters over deep page walks.

### Upgrade notes
- Existing workspaces that already installed the shipped skill bundle should run `xyte-cli skills refresh` after upgrading — the previous `getHistories` example used a wide `from=0` window that now returns 422 against production.

## [0.14.0] - 2026-08-03

### Added
Expand Down
1 change: 0 additions & 1 deletion node_modules

This file was deleted.

11 changes: 9 additions & 2 deletions skills/xyte-cli/data/public-endpoints.json
Original file line number Diff line number Diff line change
Expand Up @@ -342,7 +342,9 @@
"to",
"device_id",
"space_id",
"name"
"name",
"page",
"per_page"
],
"authScope": "organization",
"bodyType": "none",
Expand All @@ -351,7 +353,12 @@
"notes": [
"Docs page: https://docs.xyte.io/reference/get-histories",
"Docs category: /branches/2.0/categories/reference/API Access",
"Docs parent: /branches/2.0/reference/devices-2"
"Docs parent: /branches/2.0/reference/devices-2",
"from/to are Unix epoch seconds and both bounds are INCLUSIVE; the window between them must not exceed 31 days and to must be >= from (422 otherwise). Defaults (from: 1 week ago, to: now) apply independently, so send both bounds - a lone to older than a week 422s against the default from.",
"Paginate with page (starts at 1) and per_page (default 100). Values outside 1..1000 for per_page, or page < 1, are rejected with 422 - not clamped. Response includes has_next_page; no total count is returned.",
"Rows are ordered newest-first (created_at desc, stable). To read further back, set the next window's to to the current window's from - not from - 1. Both bounds are inclusive, so a row landing exactly on the boundary is returned in both windows; drop rows whose (uuid, create_at) you have already seen. Prefer a narrower window or a device_id/space_id filter over deep page walks - each page costs a full OFFSET scan, so high page numbers can time out even inside a legal window.",
"Response shape: rows are under items; each row has uuid (the device id), create_at (sic - ISO8601 string, not epoch seconds), name, space_id, model, partner, state. No row id is returned.",
"status filters by the device's CURRENT status (online, offline, unavailable, error), not the status recorded in each history row."
]
},
{
Expand Down
12 changes: 8 additions & 4 deletions skills/xyte-cli/references/endpoints.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ Derived from the bundled public endpoint spec.
| --- | --- | --- | --- |
| `organization.spaces.getSpaces` | `id`, `name`, `parent_id`, `space_type`, `created_before`, `created_after`, `path_includes` | none | Main listing endpoint with server-side filtering |
| `organization.devices.getDevices` | `page`, `per_page`, `space_id` | `page`, `per_page` | Filter devices by one space; shared docs mention `has_next_page`, live Verve/Playground responses returned `next_page`; handle either continuation field |
| `organization.devices.getHistories` | `status`, `from`, `to`, `device_id`, `space_id`, `name` | none | Filtered history lookup; can be time-windowed |
| `organization.devices.getHistories` | `status`, `from`, `to`, `device_id`, `space_id`, `name`, `page`, `per_page` | `page`, `per_page` | Filtered history lookup. `from`/`to` window must not exceed 31 days (422 otherwise); defaults: `from` = 1 week ago, `to` = now, applied independently — send both. `status` filters the device's *current* status. |
| `organization.commands.getCommands` | `status`, `page`, `per_page` | `page`, `per_page` | Command history pagination and status filter |
| `organization.incidents.getIncidents` | `from`, `to`, `status`, `priority`, `title`, `description`, `issue`, `device_model`, `partner_name`, `sub_model`, `space_id`, `page`, `per_page` | `page`, `per_page` | Incident filtering matrix. Use integer `from` and `to`; for reliable active-incident fetches use both (`from=0`, `to=<now>`). |
| `organization.notes.getAllDeviceNotes` | `page`, `per_page` | `page`, `per_page` | Paginated notes across all devices |
Expand Down Expand Up @@ -78,12 +78,16 @@ xyte-cli api call organization.devices.getHistories \
--tenant <tenant-id> \
--query-json '{
"status": "online",
"from": 0,
"to": 2000000000,
"space_id": "<space-id>"
"from": 1707400000,
"to": 1710000000,
"space_id": "<space-id>",
"page": 1,
"per_page": 100
}'
```

Replace `1710000000` with the current Unix timestamp and `1707400000` with a value at most 31 days earlier; the `from`/`to` window may not exceed 31 days (the API returns 422 for wider or reversed windows). Walk within a window with `page` (starts at 1) and `per_page` (default 100, 1..1000 — values outside that range are rejected with 422, not clamped) until `has_next_page` is false. To read further back, set the next window's `to` to the current window's `from` — not `from - 1`. Both bounds are inclusive, so a row landing exactly on the boundary is returned in both windows; drop rows whose `(uuid, create_at)` you have already seen. Rows are under `items`, newest-first; each has `uuid` (the device id) and `create_at` (sic — an ISO8601 string, not epoch seconds), and no row `id`. Prefer a shorter window or a `device_id`/`space_id` filter over deep page walks — each page costs a full OFFSET scan, so high page numbers can time out even inside a legal window.

### `organization.incidents.getIncidents`

```bash
Expand Down
6 changes: 6 additions & 0 deletions src/api-catalog/drift-overrides.json
Original file line number Diff line number Diff line change
@@ -1,4 +1,10 @@
{
"organization.devices.getHistories": {
"queryParams": ["status", "from", "to", "device_id", "space_id", "name", "page", "per_page"],
"appendNotes": [
"Public docs omit page/per_page and the 31-day from/to cap; both verified against hub histories_controller.rb (BE-1650, xyte-io/server#9302). Keep the getHistories window, pagination, ordering and response-shape notes in public-endpoints.json when regenerating."
]
},
"organization.commands.getCommands": {
"pathTemplate": "/core/v1/organization/devices/:device_id/commands",
"pathParams": [
Expand Down
11 changes: 9 additions & 2 deletions src/api-catalog/public-endpoints.json
Original file line number Diff line number Diff line change
Expand Up @@ -342,7 +342,9 @@
"to",
"device_id",
"space_id",
"name"
"name",
"page",
"per_page"
Comment thread
borisd marked this conversation as resolved.
],
"authScope": "organization",
"bodyType": "none",
Expand All @@ -351,7 +353,12 @@
"notes": [
"Docs page: https://docs.xyte.io/reference/get-histories",
"Docs category: /branches/2.0/categories/reference/API Access",
"Docs parent: /branches/2.0/reference/devices-2"
"Docs parent: /branches/2.0/reference/devices-2",
"from/to are Unix epoch seconds and both bounds are INCLUSIVE; the window between them must not exceed 31 days and to must be >= from (422 otherwise). Defaults (from: 1 week ago, to: now) apply independently, so send both bounds - a lone to older than a week 422s against the default from.",
"Paginate with page (starts at 1) and per_page (default 100). Values outside 1..1000 for per_page, or page < 1, are rejected with 422 - not clamped. Response includes has_next_page; no total count is returned.",
Comment thread
borisd marked this conversation as resolved.
"Rows are ordered newest-first (created_at desc, stable). To read further back, set the next window's to to the current window's from - not from - 1. Both bounds are inclusive, so a row landing exactly on the boundary is returned in both windows; drop rows whose (uuid, create_at) you have already seen. Prefer a narrower window or a device_id/space_id filter over deep page walks - each page costs a full OFFSET scan, so high page numbers can time out even inside a legal window.",
"Response shape: rows are under items; each row has uuid (the device id), create_at (sic - ISO8601 string, not epoch seconds), name, space_id, model, partner, state. No row id is returned.",
"status filters by the device's CURRENT status (online, offline, unavailable, error), not the status recorded in each history row."
Comment thread
borisd marked this conversation as resolved.
]
},
{
Expand Down
13 changes: 12 additions & 1 deletion tests/endpoints.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -148,7 +148,18 @@ describe('public endpoint catalog', () => {
expect(getDevices?.notes?.join(' ')).toContain('next_page');

const getHistories = endpoints.find((item) => item.key === 'organization.devices.getHistories');
expect(getHistories?.queryParams).toEqual(['status', 'from', 'to', 'device_id', 'space_id', 'name']);
expect(getHistories?.queryParams).toEqual([
'status',
'from',
'to',
'device_id',
'space_id',
'name',
'page',
'per_page'
]);
expect(getHistories?.notes?.join(' ')).toContain('31 days');
expect(getHistories?.notes?.join(' ')).toContain('has_next_page');

const getIncidents = endpoints.find((item) => item.key === 'organization.incidents.getIncidents');
expect(getIncidents?.queryParams).toEqual([
Expand Down
9 changes: 9 additions & 0 deletions tests/skills-notes-guidance.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,15 @@ describe('notes endpoint docs and skill guidance', () => {
expect(content).toContain('created_by: null');
});

it('pins getHistories window and pagination guidance in the shipped endpoint reference', () => {
const content = read('skills/xyte-cli/references/endpoints.md');

expect(content).toContain('31 days');
expect(content).toContain('has_next_page');
expect(content).toContain("set the next window's `to` to the current window's `from`");
expect(content).toContain('(uuid, create_at)');
});

it('documents generic utility behavior for note write endpoints', () => {
const docs = read('docs/ai-utility-preprocessing.md');
const skill = read('skills/xyte-cli/references/ai-utility-preprocessing.md');
Expand Down
Loading