@zeyos/client 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +31 -0
- package/LICENSE +21 -0
- package/README.md +458 -0
- package/agents/README.md +66 -0
- package/agents/shared/business-app-benchmarks.md +111 -0
- package/agents/shared/zeyos-entity-map.md +142 -0
- package/agents/shared/zeyos-entity-reference.md +570 -0
- package/agents/shared/zeyos-query-patterns.md +89 -0
- package/agents/zeyos-account-intelligence/SKILL.md +34 -0
- package/agents/zeyos-account-intelligence/agents/openai.yaml +4 -0
- package/agents/zeyos-account-intelligence/references/workflows.md +84 -0
- package/agents/zeyos-billing-insights/SKILL.md +41 -0
- package/agents/zeyos-billing-insights/agents/openai.yaml +4 -0
- package/agents/zeyos-billing-insights/references/workflows.md +106 -0
- package/agents/zeyos-campaign-and-outreach/SKILL.md +44 -0
- package/agents/zeyos-campaign-and-outreach/agents/openai.yaml +4 -0
- package/agents/zeyos-campaign-and-outreach/references/workflows.md +100 -0
- package/agents/zeyos-collaboration-and-activity/SKILL.md +37 -0
- package/agents/zeyos-collaboration-and-activity/agents/openai.yaml +4 -0
- package/agents/zeyos-collaboration-and-activity/references/workflows.md +104 -0
- package/agents/zeyos-collections-and-dunning/SKILL.md +46 -0
- package/agents/zeyos-collections-and-dunning/agents/openai.yaml +4 -0
- package/agents/zeyos-collections-and-dunning/references/workflows.md +132 -0
- package/agents/zeyos-commerce-and-inventory/SKILL.md +38 -0
- package/agents/zeyos-commerce-and-inventory/agents/openai.yaml +4 -0
- package/agents/zeyos-commerce-and-inventory/references/workflows.md +101 -0
- package/agents/zeyos-mail-operations/SKILL.md +35 -0
- package/agents/zeyos-mail-operations/agents/openai.yaml +4 -0
- package/agents/zeyos-mail-operations/references/workflows.md +110 -0
- package/agents/zeyos-notes-and-sops/SKILL.md +31 -0
- package/agents/zeyos-notes-and-sops/agents/openai.yaml +4 -0
- package/agents/zeyos-notes-and-sops/references/workflows.md +85 -0
- package/agents/zeyos-platform-and-schema/SKILL.md +37 -0
- package/agents/zeyos-platform-and-schema/agents/openai.yaml +4 -0
- package/agents/zeyos-platform-and-schema/references/workflows.md +97 -0
- package/agents/zeyos-work-management/SKILL.md +45 -0
- package/agents/zeyos-work-management/agents/openai.yaml +4 -0
- package/agents/zeyos-work-management/references/workflows.md +148 -0
- package/docs/01-api-reference/01-data-retrieval.md +601 -0
- package/docs/01-api-reference/02-authentication.md +288 -0
- package/docs/01-api-reference/03-resources.md +270 -0
- package/docs/01-api-reference/04-schema.md +539 -0
- package/docs/01-api-reference/_category_.json +9 -0
- package/docs/02-javascript-client/01-getting-started.md +146 -0
- package/docs/02-javascript-client/02-authentication.md +287 -0
- package/docs/02-javascript-client/03-making-requests.md +572 -0
- package/docs/02-javascript-client/04-practical-guide.md +348 -0
- package/docs/02-javascript-client/_category_.json +9 -0
- package/docs/03-cli/01-getting-started.md +219 -0
- package/docs/03-cli/02-commands.md +407 -0
- package/docs/03-cli/03-configuration.md +220 -0
- package/docs/03-cli/_category_.json +9 -0
- package/docs/04-agent-workflows/00-coding-agents.md +35 -0
- package/docs/04-agent-workflows/01-agent-quickstart.md +147 -0
- package/docs/04-agent-workflows/02-agent-recipes.md +109 -0
- package/docs/04-agent-workflows/03-cli-coverage-and-escalation.md +65 -0
- package/docs/04-agent-workflows/_category_.json +9 -0
- package/docs/04-sample-apps/01-kanban.md +89 -0
- package/docs/04-sample-apps/02-crm.md +81 -0
- package/docs/04-sample-apps/03-dashboard.md +80 -0
- package/docs/04-sample-apps/_category_.json +9 -0
- package/docs/05-tutorials/00-application-developers.md +43 -0
- package/docs/05-tutorials/01-integration-architecture.md +60 -0
- package/docs/05-tutorials/02-build-your-own-zeyos-frontend.md +517 -0
- package/docs/05-tutorials/03-server-side-integrations.md +185 -0
- package/docs/05-tutorials/_category_.json +9 -0
- package/docs/intro.md +197 -0
- package/openapi/api.json +24308 -0
- package/openapi/auth.json +415 -0
- package/openapi/dbref.json +56223 -0
- package/openapi/oauth2.json +781 -0
- package/openapi/sdk.json +949 -0
- package/openapi/views.txt +642 -0
- package/package.json +49 -0
- package/samples/crm/README.md +28 -0
- package/samples/crm/index.html +327 -0
- package/samples/crm/js/api.js +208 -0
- package/samples/crm/js/auth.js +61 -0
- package/samples/crm/js/main.js +545 -0
- package/samples/crm/js/state.js +90 -0
- package/samples/crm/js/ui.js +51 -0
- package/samples/dashboard/README.md +28 -0
- package/samples/dashboard/index.html +280 -0
- package/samples/dashboard/js/api.js +197 -0
- package/samples/dashboard/js/auth.js +59 -0
- package/samples/dashboard/js/main.js +382 -0
- package/samples/dashboard/js/state.js +81 -0
- package/samples/dashboard/js/ui.js +48 -0
- package/samples/kanban/README.md +28 -0
- package/samples/kanban/index.html +263 -0
- package/samples/kanban/js/api.js +152 -0
- package/samples/kanban/js/auth.js +59 -0
- package/samples/kanban/js/constants.js +40 -0
- package/samples/kanban/js/kanban.js +246 -0
- package/samples/kanban/js/main.js +362 -0
- package/samples/kanban/js/modals.js +474 -0
- package/samples/kanban/js/settings.js +82 -0
- package/samples/kanban/js/state.js +118 -0
- package/samples/kanban/js/ui.js +49 -0
- package/scripts/generate-client.mjs +344 -0
- package/src/generated/operations.js +9772 -0
- package/src/generated/schema.js +8982 -0
- package/src/index.js +85 -0
- package/src/runtime/client.js +1208 -0
- package/src/runtime/error.js +29 -0
- package/src/runtime/http.js +174 -0
- package/src/runtime/request-shape.js +35 -0
- package/src/runtime/schema.js +206 -0
- package/src/runtime/suggest.js +74 -0
- package/src/runtime/token-store.js +105 -0
|
@@ -0,0 +1,572 @@
|
|
|
1
|
+
---
|
|
2
|
+
sidebar_label: Making Requests
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
# Making API Requests
|
|
6
|
+
|
|
7
|
+
The ZeyOS client generates methods for every REST operation defined in the ZeyOS OpenAPI specification. These methods provide a clean, high-level interface for all CRUD operations, filtering, sorting, and pagination.
|
|
8
|
+
|
|
9
|
+
## Generated Methods
|
|
10
|
+
|
|
11
|
+
All standard API operations are available as `client.api.<operationId>(input, options?)`. The `operationId` corresponds directly to the operation name in the ZeyOS OpenAPI specification.
|
|
12
|
+
|
|
13
|
+
```js
|
|
14
|
+
// Examples of generated methods
|
|
15
|
+
client.api.listTickets(...)
|
|
16
|
+
client.api.getTicket(...)
|
|
17
|
+
client.api.createTicket(...)
|
|
18
|
+
client.api.updateTicket(...)
|
|
19
|
+
client.api.deleteTicket(...)
|
|
20
|
+
client.api.listAccounts(...)
|
|
21
|
+
client.api.createTask(...)
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
Each method accepts an `input` object where you can mix path parameters, query parameters, and request body fields in a single flat object. The client automatically routes each property to the correct location based on the operation's parameter definitions.
|
|
25
|
+
|
|
26
|
+
## CRUD Operations
|
|
27
|
+
|
|
28
|
+
### List Records
|
|
29
|
+
|
|
30
|
+
Retrieve collections of records with optional filters, sorting, and pagination:
|
|
31
|
+
|
|
32
|
+
```js
|
|
33
|
+
const tickets = await client.api.listTickets({
|
|
34
|
+
fields: ['ID', 'name', 'status', 'priority', 'duedate'],
|
|
35
|
+
filters: { status: 1, visibility: 0 },
|
|
36
|
+
sort: ['-lastmodified'],
|
|
37
|
+
limit: 50,
|
|
38
|
+
});
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
### Get a Single Record
|
|
42
|
+
|
|
43
|
+
Fetch a specific record by ID. Additional flags control which related data is included:
|
|
44
|
+
|
|
45
|
+
```js
|
|
46
|
+
const ticket = await client.api.getTicket({
|
|
47
|
+
ID: 42,
|
|
48
|
+
extdata: 1,
|
|
49
|
+
tags: 1,
|
|
50
|
+
});
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
### Create a Record
|
|
54
|
+
|
|
55
|
+
Create a new record by passing the required fields. For operations **without** path parameters (like create), you can pass all fields as a flat object:
|
|
56
|
+
|
|
57
|
+
```js
|
|
58
|
+
const newTicket = await client.api.createTicket({
|
|
59
|
+
name: 'Fix login bug',
|
|
60
|
+
status: 0,
|
|
61
|
+
priority: 3,
|
|
62
|
+
description: 'Users cannot log in with SSO',
|
|
63
|
+
visibility: 0,
|
|
64
|
+
});
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
:::caution Required fields the spec does not declare
|
|
68
|
+
Some columns are `NOT NULL` with no database default, so a create that omits them fails server-side with an opaque HTTP 500 — even though the OpenAPI spec marks nothing as required. Most notably, **creating an `account` requires a `currency`** (e.g. `"EUR"`). `client.schema.validate('createAccount', …)` now flags this before you send it.
|
|
69
|
+
|
|
70
|
+
```js
|
|
71
|
+
await client.api.createAccount({ lastname: 'Acme Corp', type: 1, currency: 'EUR' });
|
|
72
|
+
```
|
|
73
|
+
:::
|
|
74
|
+
|
|
75
|
+
### Update a Record
|
|
76
|
+
|
|
77
|
+
Update an existing record with a PATCH request. Pass the `ID` and changed fields in one object:
|
|
78
|
+
|
|
79
|
+
```js
|
|
80
|
+
await client.api.updateTicket({
|
|
81
|
+
ID: 42,
|
|
82
|
+
status: 4,
|
|
83
|
+
priority: 4,
|
|
84
|
+
});
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
:::tip Explicit body is also supported
|
|
88
|
+
Use `body` or `data` when you want to separate URL parameters from payload fields manually:
|
|
89
|
+
|
|
90
|
+
```js
|
|
91
|
+
await client.api.updateTicket({ ID: 42, body: { status: 4, priority: 4 } });
|
|
92
|
+
```
|
|
93
|
+
:::
|
|
94
|
+
|
|
95
|
+
The PATCH response body contains the full updated record. Use it to confirm the server applied your changes:
|
|
96
|
+
|
|
97
|
+
```js
|
|
98
|
+
const updated = await client.api.updateTicket({
|
|
99
|
+
ID: 42,
|
|
100
|
+
status: 4,
|
|
101
|
+
});
|
|
102
|
+
console.log(updated.status); // 4 -- confirmed by the server
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
### Delete a Record
|
|
106
|
+
|
|
107
|
+
Delete a record by ID:
|
|
108
|
+
|
|
109
|
+
```js
|
|
110
|
+
await client.api.deleteTicket({ ID: 42 });
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
### Check Existence
|
|
114
|
+
|
|
115
|
+
Use a HEAD request to check whether a record exists without downloading the full response body:
|
|
116
|
+
|
|
117
|
+
```js
|
|
118
|
+
const exists = await client.api.existsTicket({ ID: 42 });
|
|
119
|
+
// Returns true if the record exists (2xx/3xx), throws ZeyosApiError on 404
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
## Field Selection
|
|
123
|
+
|
|
124
|
+
Control which fields are returned in list responses. This reduces payload size and improves performance.
|
|
125
|
+
|
|
126
|
+
### Array Form
|
|
127
|
+
|
|
128
|
+
Pass an array of field names to return only those fields:
|
|
129
|
+
|
|
130
|
+
```js
|
|
131
|
+
const result = await client.api.listAccounts({
|
|
132
|
+
fields: ['ID', 'lastname', 'contact.city'],
|
|
133
|
+
filters: { visibility: 0 },
|
|
134
|
+
});
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
### Object Form (with Aliases)
|
|
138
|
+
|
|
139
|
+
Pass an object to rename fields in the response. Keys become the output names, values are the source field paths:
|
|
140
|
+
|
|
141
|
+
```js
|
|
142
|
+
const result = await client.api.listAccounts({
|
|
143
|
+
fields: {
|
|
144
|
+
'Id': 'ID',
|
|
145
|
+
'Name': 'lastname',
|
|
146
|
+
'City': 'contact.city',
|
|
147
|
+
'Agent': 'assigneduser.name',
|
|
148
|
+
},
|
|
149
|
+
filters: { visibility: 0 },
|
|
150
|
+
});
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
:::note
|
|
154
|
+
Dot-notation field paths (e.g. `contact.city`, `assigneduser.name`) allow you to select fields from related or nested objects.
|
|
155
|
+
:::
|
|
156
|
+
|
|
157
|
+
## Filtering
|
|
158
|
+
|
|
159
|
+
ZeyOS provides two filter parameters. Use `filters` (plural) for the broadest compatibility -- it works with both scalar fields and GIN-indexed foreign key fields:
|
|
160
|
+
|
|
161
|
+
```js
|
|
162
|
+
// Standard filtering -- works for all field types
|
|
163
|
+
const active = await client.api.listTickets({
|
|
164
|
+
filters: { status: 4, visibility: 0 },
|
|
165
|
+
});
|
|
166
|
+
|
|
167
|
+
// Filter by foreign key field (e.g. project, account)
|
|
168
|
+
const projectTickets = await client.api.listTickets({
|
|
169
|
+
filters: { visibility: 0, project: projectId },
|
|
170
|
+
});
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
### `filter` vs `filters`
|
|
174
|
+
|
|
175
|
+
| Parameter | Supports | Notes |
|
|
176
|
+
|-----------|----------|-------|
|
|
177
|
+
| `filter` | Scalar fields (status, visibility, priority) | Defined in the OpenAPI spec. May not work for all FK fields. |
|
|
178
|
+
| `filters` | All field types including GIN-indexed foreign keys (project, account, ticket) | Recommended for general use -- handles both scalar and FK fields. |
|
|
179
|
+
|
|
180
|
+
:::tip
|
|
181
|
+
When in doubt, use `filters` (plural). Using `filter` (singular) with a foreign-key field like `project` silently returns unfiltered results rather than throwing an error, which makes problems hard to spot.
|
|
182
|
+
:::
|
|
183
|
+
|
|
184
|
+
### Full-Text Search
|
|
185
|
+
|
|
186
|
+
Use the `query` parameter to search across a resource's indexed text fields:
|
|
187
|
+
|
|
188
|
+
```js
|
|
189
|
+
const results = await client.api.listAccounts({
|
|
190
|
+
fields: ['ID', 'lastname', 'contact.email'],
|
|
191
|
+
filters: { visibility: 0 },
|
|
192
|
+
query: 'acme',
|
|
193
|
+
limit: 20,
|
|
194
|
+
});
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
## Distinct Results
|
|
198
|
+
|
|
199
|
+
Pass `distinct: true` to deduplicate result rows. This is useful when using dot-notation joins that may produce multiple rows per record:
|
|
200
|
+
|
|
201
|
+
```js
|
|
202
|
+
const result = await client.api.listAccounts({
|
|
203
|
+
distinct: true,
|
|
204
|
+
fields: ['ID', 'lastname', 'contact.country'],
|
|
205
|
+
filters: { visibility: 0 },
|
|
206
|
+
});
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
## Sorting
|
|
210
|
+
|
|
211
|
+
Pass an array of field names prefixed with `+` (ascending) or `-` (descending):
|
|
212
|
+
|
|
213
|
+
```js
|
|
214
|
+
// Sort by last modified, newest first
|
|
215
|
+
const tickets = await client.api.listTickets({
|
|
216
|
+
sort: ['-lastmodified'],
|
|
217
|
+
});
|
|
218
|
+
|
|
219
|
+
// Multi-field sort: priority descending, then name ascending
|
|
220
|
+
const tickets = await client.api.listTickets({
|
|
221
|
+
sort: ['-priority', '+name'],
|
|
222
|
+
});
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
## Pagination
|
|
226
|
+
|
|
227
|
+
Use `limit` and `offset` to page through large result sets:
|
|
228
|
+
|
|
229
|
+
```js
|
|
230
|
+
// Get total count first
|
|
231
|
+
const countResult = await client.api.listTickets({
|
|
232
|
+
count: true,
|
|
233
|
+
filters: { status: 1, visibility: 0 },
|
|
234
|
+
});
|
|
235
|
+
// countResult contains the total number of matching records
|
|
236
|
+
|
|
237
|
+
// Fetch the first page
|
|
238
|
+
const page1 = await client.api.listTickets({
|
|
239
|
+
limit: 50,
|
|
240
|
+
offset: 0,
|
|
241
|
+
filters: { status: 1, visibility: 0 },
|
|
242
|
+
});
|
|
243
|
+
|
|
244
|
+
// Fetch the second page
|
|
245
|
+
const page2 = await client.api.listTickets({
|
|
246
|
+
limit: 50,
|
|
247
|
+
offset: 50,
|
|
248
|
+
filters: { status: 1, visibility: 0 },
|
|
249
|
+
});
|
|
250
|
+
```
|
|
251
|
+
|
|
252
|
+
:::tip
|
|
253
|
+
Use `count: true` to get the total number of matching records without fetching the full dataset. This is useful for building pagination controls.
|
|
254
|
+
:::
|
|
255
|
+
|
|
256
|
+
## Normalising List Responses
|
|
257
|
+
|
|
258
|
+
List endpoints are not uniform across the full surface area. Depending on the endpoint and response mode, you may see:
|
|
259
|
+
|
|
260
|
+
- a plain array
|
|
261
|
+
- an object wrapper with `data`
|
|
262
|
+
- count metadata alongside the payload when `count: true` is used
|
|
263
|
+
|
|
264
|
+
The normalization helpers are useful when you want call sites to share one response-shape contract:
|
|
265
|
+
|
|
266
|
+
```js
|
|
267
|
+
import { normalizeCountResult, normalizeListResult } from '@zeyos/client';
|
|
268
|
+
|
|
269
|
+
// Without count -- result is a plain array
|
|
270
|
+
const raw = await client.api.listTickets({ filters: { visibility: 0 } });
|
|
271
|
+
const { data } = normalizeListResult(raw);
|
|
272
|
+
// data is always an array
|
|
273
|
+
|
|
274
|
+
// With count metadata -- result may include both data and count
|
|
275
|
+
const raw2 = await client.api.listTickets({ filters: { visibility: 0 }, count: true });
|
|
276
|
+
const { data: tickets, count } = normalizeListResult(raw2);
|
|
277
|
+
// tickets: array, count: number
|
|
278
|
+
|
|
279
|
+
// Count-only request -- result may be a number or an object with count metadata
|
|
280
|
+
const countOnly = await client.api.listTickets({ filters: { visibility: 0 }, count: true });
|
|
281
|
+
const total = normalizeCountResult(countOnly);
|
|
282
|
+
```
|
|
283
|
+
|
|
284
|
+
## Extended Data
|
|
285
|
+
|
|
286
|
+
Many ZeyOS entities support **extended data** (extdata) -- custom fields defined through the platform's extensibility features. By default, extended data fields are not included in API responses. To include them, pass `extdata: 1` as a parameter.
|
|
287
|
+
|
|
288
|
+
### Including Extended Data in List Requests
|
|
289
|
+
|
|
290
|
+
For list operations, `extdata` is sent as a body parameter:
|
|
291
|
+
|
|
292
|
+
```js
|
|
293
|
+
const tickets = await client.api.listTickets({
|
|
294
|
+
fields: ['ID', 'name', 'status', 'priority', 'duedate'],
|
|
295
|
+
filters: { status: 1, visibility: 0 },
|
|
296
|
+
sort: ['-lastmodified'],
|
|
297
|
+
limit: 50,
|
|
298
|
+
extdata: 1,
|
|
299
|
+
});
|
|
300
|
+
```
|
|
301
|
+
|
|
302
|
+
### Including Extended Data in GET Requests
|
|
303
|
+
|
|
304
|
+
For single-record GET operations, `extdata` is sent as a query parameter:
|
|
305
|
+
|
|
306
|
+
```js
|
|
307
|
+
const ticket = await client.api.getTicket({
|
|
308
|
+
ID: 42,
|
|
309
|
+
extdata: 1,
|
|
310
|
+
tags: 1,
|
|
311
|
+
});
|
|
312
|
+
```
|
|
313
|
+
|
|
314
|
+
### Selecting Specific Extended Data Fields
|
|
315
|
+
|
|
316
|
+
You can reference individual extended data fields using dot notation in the `fields` parameter. Use the `extdata.fieldname` syntax to select only the custom fields you need:
|
|
317
|
+
|
|
318
|
+
```js
|
|
319
|
+
const tickets = await client.api.listTickets({
|
|
320
|
+
fields: {
|
|
321
|
+
'Id': 'ID',
|
|
322
|
+
'Name': 'name',
|
|
323
|
+
'Region': 'extdata.region',
|
|
324
|
+
'CustomerType': 'extdata.customer_type',
|
|
325
|
+
},
|
|
326
|
+
filters: { status: 1, visibility: 0 },
|
|
327
|
+
limit: 50,
|
|
328
|
+
});
|
|
329
|
+
```
|
|
330
|
+
|
|
331
|
+
:::note
|
|
332
|
+
When you select specific `extdata.*` fields via the `fields` parameter, you do not need to pass `extdata: 1` separately -- the selected fields will be included automatically.
|
|
333
|
+
:::
|
|
334
|
+
|
|
335
|
+
## Expanding JSON and Binary Columns
|
|
336
|
+
|
|
337
|
+
The `expand` parameter is used to inline the contents of **JSON columns** or **binary/file columns** that are normally returned as references or omitted for performance reasons. This applies to structured data columns such as `binfile` on messages, `items` on transactions, or `data` on objects.
|
|
338
|
+
|
|
339
|
+
```js
|
|
340
|
+
// Expand the binary file content of a message
|
|
341
|
+
const message = await client.api.getMessage({
|
|
342
|
+
ID: 123,
|
|
343
|
+
expand: ['binfile'],
|
|
344
|
+
});
|
|
345
|
+
|
|
346
|
+
// Expand the items array on a transaction
|
|
347
|
+
const transaction = await client.api.getTransaction({
|
|
348
|
+
ID: 456,
|
|
349
|
+
expand: ['items'],
|
|
350
|
+
});
|
|
351
|
+
```
|
|
352
|
+
|
|
353
|
+
:::caution
|
|
354
|
+
Do not confuse `expand` with `extdata`. The `expand` parameter is strictly for JSON and binary columns -- it does not apply to extended data fields. To include extended data, use `extdata: 1` instead.
|
|
355
|
+
:::
|
|
356
|
+
|
|
357
|
+
## Schema Introspection and Validation
|
|
358
|
+
|
|
359
|
+
The `client.schema` surface lets you (or an AI agent) discover the data model and validate inputs **without any network call** -- it is built from the generated schema. This is the fastest way to learn fields, types, foreign keys, and enum values before issuing a request.
|
|
360
|
+
|
|
361
|
+
```js
|
|
362
|
+
client.schema.resources(); // all resource names
|
|
363
|
+
client.schema.describe('tickets'); // { name, type, fields: { status: { type, enum }, account: { fk }, … } }
|
|
364
|
+
client.schema.fields('accounts'); // ['ID', 'lastname', 'firstname', 'type', …]
|
|
365
|
+
client.schema.operations('tickets'); // ['listTickets', 'getTicket', 'createTicket', …]
|
|
366
|
+
client.schema.resourceForOperation('listDunningNotices'); // 'dunning'
|
|
367
|
+
client.schema.suggestOperation('listDunning'); // 'listDunningNotices'
|
|
368
|
+
```
|
|
369
|
+
|
|
370
|
+
### Validating a Call
|
|
371
|
+
|
|
372
|
+
`client.schema.validate(operationId, input)` returns structured, self-correcting hints. It never throws and is lenient about dot-notation joins (`contact.city`) and extended fields (`extdata.*`):
|
|
373
|
+
|
|
374
|
+
```js
|
|
375
|
+
const result = client.schema.validate('createAccount', { name: 'Acme' });
|
|
376
|
+
// {
|
|
377
|
+
// valid: false,
|
|
378
|
+
// errors: [{ field: 'name', message: 'Unknown field "name". Did you mean "lastname"?', suggestion: 'lastname' }]
|
|
379
|
+
// }
|
|
380
|
+
```
|
|
381
|
+
|
|
382
|
+
It flags unknown fields (with a suggestion), `filter` used where `filters` is preferred, invalid enum values (listing the valid set), and missing required create fields. The ZeyOS spec carries no required-field metadata, so a curated supplement covers known NOT-NULL-without-default columns — notably `accounts` require `currency`:
|
|
383
|
+
|
|
384
|
+
```js
|
|
385
|
+
client.schema.validate('createAccount', { lastname: 'Acme' });
|
|
386
|
+
// { valid: false, errors: [{ field: 'currency', message: 'Missing required field "currency" for accounts …', suggestion: 'currency' }] }
|
|
387
|
+
```
|
|
388
|
+
|
|
389
|
+
### Pre-flight Validation
|
|
390
|
+
|
|
391
|
+
Set `validate: true` on the client to validate every request before it is sent and throw a `ZeyosValidationError` (rather than letting the server reject it):
|
|
392
|
+
|
|
393
|
+
```js
|
|
394
|
+
import { ZeyosValidationError } from '@zeyos/client';
|
|
395
|
+
|
|
396
|
+
const client = createZeyosClient({ platform: 'live', instance: 'demo', validate: true });
|
|
397
|
+
|
|
398
|
+
try {
|
|
399
|
+
await client.api.createAccount({ name: 'Acme' }); // wrong field
|
|
400
|
+
} catch (err) {
|
|
401
|
+
if (err instanceof ZeyosValidationError) {
|
|
402
|
+
console.log(err.operationId); // 'createAccount'
|
|
403
|
+
console.log(err.errors); // structured hints (see above)
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
```
|
|
407
|
+
|
|
408
|
+
Validation is **off by default** so that custom and extended fields are never blocked; enable it in agent or development workflows where fast, descriptive feedback is more valuable.
|
|
409
|
+
|
|
410
|
+
## Retries and Rate Limiting
|
|
411
|
+
|
|
412
|
+
The client automatically retries transient failures. By default it retries `429 Too Many Requests` and `503 Service Unavailable` up to twice, using exponential backoff with jitter and honoring a `Retry-After` header when present. Retries are abort-aware (an `AbortSignal` cancels pending waits).
|
|
413
|
+
|
|
414
|
+
```js
|
|
415
|
+
// Customize the policy
|
|
416
|
+
const client = createZeyosClient({
|
|
417
|
+
platform: 'live',
|
|
418
|
+
instance: 'demo',
|
|
419
|
+
retry: {
|
|
420
|
+
maxRetries: 3,
|
|
421
|
+
retryOn: [429, 503], // statuses to retry
|
|
422
|
+
baseDelayMs: 300, // backoff base
|
|
423
|
+
maxDelayMs: 10000 // cap per wait
|
|
424
|
+
}
|
|
425
|
+
});
|
|
426
|
+
|
|
427
|
+
// Disable retries entirely
|
|
428
|
+
const noRetry = createZeyosClient({ platform: 'live', instance: 'demo', retry: false });
|
|
429
|
+
```
|
|
430
|
+
|
|
431
|
+
:::note
|
|
432
|
+
Only `429`/`503` are retried by default -- statuses that clearly mean "try again later". `5xx` codes such as `500`/`502` are **not** retried automatically, to avoid re-applying a non-idempotent write that may have partially succeeded. Add them to `retryOn` only for read-heavy workloads.
|
|
433
|
+
:::
|
|
434
|
+
|
|
435
|
+
## Error Handling
|
|
436
|
+
|
|
437
|
+
All API errors are thrown as `ZeyosApiError` instances. This class extends `Error` and includes structured information about the failed request.
|
|
438
|
+
|
|
439
|
+
```js
|
|
440
|
+
import { ZeyosApiError } from '@zeyos/client';
|
|
441
|
+
|
|
442
|
+
try {
|
|
443
|
+
await client.api.getTicket({ ID: 999 });
|
|
444
|
+
} catch (err) {
|
|
445
|
+
if (err instanceof ZeyosApiError) {
|
|
446
|
+
console.log(err.status); // 404
|
|
447
|
+
console.log(err.statusText); // 'Not Found'
|
|
448
|
+
console.log(err.operationId); // 'getTicket'
|
|
449
|
+
console.log(err.service); // 'api'
|
|
450
|
+
console.log(err.method); // 'GET'
|
|
451
|
+
console.log(err.url); // Full request URL
|
|
452
|
+
console.log(err.body); // Error response body (parsed JSON or text)
|
|
453
|
+
}
|
|
454
|
+
}
|
|
455
|
+
```
|
|
456
|
+
|
|
457
|
+
The `ZeyosApiError` properties:
|
|
458
|
+
|
|
459
|
+
`JsonValue` means a parsed JSON scalar, array, or plain JSON object. Non-JSON error responses are returned as strings.
|
|
460
|
+
|
|
461
|
+
| Property | Type | Description |
|
|
462
|
+
|----------|------|-------------|
|
|
463
|
+
| `status` | `number` | HTTP status code (e.g. `404`, `401`, `500`) |
|
|
464
|
+
| `statusText` | `string` | HTTP status text (e.g. `'Not Found'`) |
|
|
465
|
+
| `operationId` | `string` | The operation that failed (e.g. `'getTicket'`) |
|
|
466
|
+
| `service` | `string` | The service key (e.g. `'api'`, `'oauth2'`) |
|
|
467
|
+
| `method` | `string` | HTTP method used (e.g. `'GET'`, `'POST'`) |
|
|
468
|
+
| `url` | `string` | The full request URL |
|
|
469
|
+
| `body` | `JsonValue` | The parsed JSON response body, plain-text error body, or `null` |
|
|
470
|
+
| `headers` | `Record<string,string>` | Response headers as a plain object |
|
|
471
|
+
|
|
472
|
+
### Unknown Operations
|
|
473
|
+
|
|
474
|
+
Calling an operation that does not exist rejects with a `ZeyosApiError` that suggests the closest match, instead of an opaque `is not a function` error -- useful when an operationId differs from the underlying table name:
|
|
475
|
+
|
|
476
|
+
```js
|
|
477
|
+
await client.api.listDunning({});
|
|
478
|
+
// ZeyosApiError: Unknown operation 'api.listDunning'. Did you mean 'listDunningNotices'?
|
|
479
|
+
```
|
|
480
|
+
|
|
481
|
+
### Validation Errors
|
|
482
|
+
|
|
483
|
+
When the client is created with `validate: true`, malformed requests reject with a `ZeyosValidationError` **before** any network call (see [Schema Introspection and Validation](#schema-introspection-and-validation)). It carries `operationId` and a structured `errors` array.
|
|
484
|
+
|
|
485
|
+
## Low-Level Requests
|
|
486
|
+
|
|
487
|
+
For endpoints not covered by the generated methods, or when you need full control over the request, use `client.request()`.
|
|
488
|
+
|
|
489
|
+
### By Operation ID
|
|
490
|
+
|
|
491
|
+
Reference a known operation by its service and operation ID:
|
|
492
|
+
|
|
493
|
+
```js
|
|
494
|
+
const result = await client.request({
|
|
495
|
+
service: 'api',
|
|
496
|
+
operationId: 'listTickets',
|
|
497
|
+
body: { filters: { status: 1, visibility: 0 }, limit: 10 },
|
|
498
|
+
});
|
|
499
|
+
```
|
|
500
|
+
|
|
501
|
+
### By Path and Method
|
|
502
|
+
|
|
503
|
+
Specify the HTTP method and path directly for custom or undocumented endpoints:
|
|
504
|
+
|
|
505
|
+
```js
|
|
506
|
+
const result = await client.request({
|
|
507
|
+
service: 'api',
|
|
508
|
+
method: 'POST',
|
|
509
|
+
path: '/tickets/',
|
|
510
|
+
body: { filters: { status: 1, visibility: 0 } },
|
|
511
|
+
});
|
|
512
|
+
```
|
|
513
|
+
|
|
514
|
+
### Raw Responses
|
|
515
|
+
|
|
516
|
+
Pass `raw: true` to receive the full response envelope instead of just the parsed body:
|
|
517
|
+
|
|
518
|
+
```js
|
|
519
|
+
const response = await client.request({
|
|
520
|
+
service: 'api',
|
|
521
|
+
operationId: 'listTickets',
|
|
522
|
+
body: { limit: 10 },
|
|
523
|
+
raw: true,
|
|
524
|
+
});
|
|
525
|
+
|
|
526
|
+
console.log(response.status); // 200
|
|
527
|
+
console.log(response.headers); // Response headers
|
|
528
|
+
console.log(response.data); // Parsed body
|
|
529
|
+
```
|
|
530
|
+
|
|
531
|
+
## Request Options
|
|
532
|
+
|
|
533
|
+
All generated methods and `client.request()` accept an optional second argument with request-level options:
|
|
534
|
+
|
|
535
|
+
| Option | Type | Description |
|
|
536
|
+
|--------|------|-------------|
|
|
537
|
+
| `signal` | `AbortSignal` | An `AbortController` signal to cancel the request |
|
|
538
|
+
| `raw` | `boolean` | Return the full response envelope instead of just the data |
|
|
539
|
+
| `auth` | `string \| { mode?: string, accessToken?: string, access_token?: string, refreshToken?: string, refresh_token?: string, clientId?: string, client_id?: string, clientSecret?: string, client_secret?: string }` | Override the authentication mode or credentials for this request |
|
|
540
|
+
| `baseUrl` | `string` | Override the base URL for this request |
|
|
541
|
+
| `bodyType` | `'json' \| 'form'` | Force a body encoding |
|
|
542
|
+
|
|
543
|
+
Example with an abort controller:
|
|
544
|
+
|
|
545
|
+
```js
|
|
546
|
+
const controller = new AbortController();
|
|
547
|
+
|
|
548
|
+
// Cancel after 5 seconds
|
|
549
|
+
setTimeout(() => controller.abort(), 5000);
|
|
550
|
+
|
|
551
|
+
const tickets = await client.api.listTickets(
|
|
552
|
+
{ limit: 100 },
|
|
553
|
+
{ signal: controller.signal }
|
|
554
|
+
);
|
|
555
|
+
```
|
|
556
|
+
|
|
557
|
+
:::tip Persisting refreshed tokens
|
|
558
|
+
When using token mode with `autoRefresh` in a trusted environment, tokens are updated in the token store automatically. If you use a `MemoryTokenStore`, those refreshed tokens will be lost on page reload. Use the `syncTokens` pattern to persist them:
|
|
559
|
+
|
|
560
|
+
```js
|
|
561
|
+
async function syncTokens() {
|
|
562
|
+
const tokenSet = await client.auth.getTokenSet();
|
|
563
|
+
if (tokenSet?.accessToken) {
|
|
564
|
+
localStorage.setItem('zeyos_tokens', JSON.stringify(tokenSet));
|
|
565
|
+
}
|
|
566
|
+
}
|
|
567
|
+
|
|
568
|
+
// Call after important API operations
|
|
569
|
+
const tickets = await client.api.listTickets({ limit: 50 });
|
|
570
|
+
await syncTokens();
|
|
571
|
+
```
|
|
572
|
+
:::
|