@openluxeco/cli 0.1.0 → 0.5.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/README.md +89 -2
- package/package.json +1 -1
- package/src/auth.js +5 -0
- package/src/cli.js +137 -2
- package/src/config.js +5 -0
- package/src/mcp.js +188 -0
- package/src/resources.js +594 -1
package/README.md
CHANGED
|
@@ -38,6 +38,46 @@ openluxe auth logout # forget the local token
|
|
|
38
38
|
|
|
39
39
|
Revoke a CLI token anytime from **Settings → Integrations** on the web.
|
|
40
40
|
|
|
41
|
+
## Terms of Service
|
|
42
|
+
|
|
43
|
+
Use of the API and this CLI is governed by a binding agreement. Before any
|
|
44
|
+
request will succeed you must read and accept the **OpenLuxe API & CLI Terms
|
|
45
|
+
of Service** and every referenced policy (Terms of Service, Privacy, Acceptable
|
|
46
|
+
Use, Data, Cookie). This is a one-time, in-browser step — authorizing the
|
|
47
|
+
device during `openluxe auth login` records your acceptance. You are
|
|
48
|
+
re-prompted only if the terms materially change (e.g. pricing, rate limits,
|
|
49
|
+
discontinued endpoints).
|
|
50
|
+
|
|
51
|
+
If a token predates the current terms (or they changed), every call returns
|
|
52
|
+
`403` and the CLI tells you exactly where to accept. Review anytime:
|
|
53
|
+
|
|
54
|
+
```bash
|
|
55
|
+
openluxe terms
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
Key points: the API and CLI are provided "as is" with no uptime guarantee;
|
|
59
|
+
endpoints, pricing, credits, and rate limits may change or be discontinued at
|
|
60
|
+
any time without liability; AI-generated output may be inaccurate and must be
|
|
61
|
+
independently verified. Full text: `https://openluxe.co/api-terms`.
|
|
62
|
+
|
|
63
|
+
## Pro access
|
|
64
|
+
|
|
65
|
+
The OpenLuxe API, this CLI, and the MCP server are part of the **Pro suite** —
|
|
66
|
+
using them requires unlocking Pro access on your account (the same one-time
|
|
67
|
+
platform access that gates the Pro apps in the web app). Until you unlock it,
|
|
68
|
+
commands return:
|
|
69
|
+
|
|
70
|
+
```
|
|
71
|
+
✗ Pro access required
|
|
72
|
+
The OpenLuxe API, CLI, and MCP server are part of the Pro suite.
|
|
73
|
+
Unlock Pro access ($99 today) to use them.
|
|
74
|
+
|
|
75
|
+
Unlock here: https://openluxe.co/onboarding/access
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
Authorizing the CLI (`openluxe auth login`) is also blocked in the browser until
|
|
79
|
+
you unlock access. Admins and grandfathered accounts are exempt.
|
|
80
|
+
|
|
41
81
|
## Use it
|
|
42
82
|
|
|
43
83
|
Typed resource commands:
|
|
@@ -51,8 +91,20 @@ openluxe webhooks events
|
|
|
51
91
|
openluxe me show
|
|
52
92
|
```
|
|
53
93
|
|
|
54
|
-
|
|
55
|
-
|
|
94
|
+
AI generation is async — start a job, then poll the returned handle:
|
|
95
|
+
|
|
96
|
+
```bash
|
|
97
|
+
# start (returns { data: { id, status, status_url } } with HTTP 202)
|
|
98
|
+
openluxe generate start image -d '{"prompt":"a marble villa at golden hour"}'
|
|
99
|
+
openluxe generate start agent_odysseus -d '{"message":"brief me before the meeting"}'
|
|
100
|
+
|
|
101
|
+
# poll until status is succeeded | failed
|
|
102
|
+
openluxe generations get <id>
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
Generations are charged the same credits as the equivalent in-app action; a
|
|
106
|
+
shortfall returns `402` with a `topup_url`. Run `openluxe <resource>` to see
|
|
107
|
+
its commands, or `openluxe help` for the full list.
|
|
56
108
|
|
|
57
109
|
Raw passthrough to any v1 endpoint:
|
|
58
110
|
|
|
@@ -73,6 +125,41 @@ scripts and tool-use loops.
|
|
|
73
125
|
export OPENLUXE_API_URL=https://openluxe.co # default; override for staging/local
|
|
74
126
|
```
|
|
75
127
|
|
|
128
|
+
## MCP (Model Context Protocol)
|
|
129
|
+
|
|
130
|
+
`openluxe mcp` runs an MCP server over stdio, so any MCP-aware client
|
|
131
|
+
(Claude Code, Claude Desktop, Cursor, …) can drive OpenLuxe in a chat. It
|
|
132
|
+
authenticates with your stored CLI token and proxies to the v1 API — the agent
|
|
133
|
+
gets the same scoped, ToS-gated, billed surface you do.
|
|
134
|
+
|
|
135
|
+
Sign in once (`openluxe auth login`), then register the server with your client.
|
|
136
|
+
Example MCP client config:
|
|
137
|
+
|
|
138
|
+
```json
|
|
139
|
+
{
|
|
140
|
+
"mcpServers": {
|
|
141
|
+
"openluxe": { "command": "openluxe", "args": ["mcp"] }
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
It exposes a lean tool set — `openluxe_list_endpoints` to discover the full v1
|
|
147
|
+
surface and `openluxe_api_request` to call any of it, plus typed helpers
|
|
148
|
+
(`openluxe_me`, `openluxe_search`, `openluxe_contacts_list`,
|
|
149
|
+
`openluxe_create_contact`, `openluxe_credits_balance`). OpenLuxe also hosts a
|
|
150
|
+
remote MCP server at `https://openluxe.co/api/v1/mcp` (Bearer-authed with a
|
|
151
|
+
`ol_itk_` token) for clients that speak Streamable HTTP directly.
|
|
152
|
+
|
|
153
|
+
## Credits
|
|
154
|
+
|
|
155
|
+
AI-generating calls (`openluxe generate …`) cost credits at web-app parity. A
|
|
156
|
+
shortfall returns `402` with a top-up link; you can also open it anytime:
|
|
157
|
+
|
|
158
|
+
```bash
|
|
159
|
+
openluxe credits balance
|
|
160
|
+
openluxe credits buy
|
|
161
|
+
```
|
|
162
|
+
|
|
76
163
|
## Environment
|
|
77
164
|
|
|
78
165
|
| Var | Purpose |
|
package/package.json
CHANGED
package/src/auth.js
CHANGED
|
@@ -54,6 +54,11 @@ export async function login({ base } = {}) {
|
|
|
54
54
|
const who = poll.data.user?.email || poll.data.user?.name || 'your account';
|
|
55
55
|
console.log(`\x1b[32m✓ Signed in as ${who}\x1b[0m`);
|
|
56
56
|
console.log(` Token stored at ${credentialsPath}`);
|
|
57
|
+
console.log('');
|
|
58
|
+
console.log(' Your API use is governed by the OpenLuxe API & CLI Terms');
|
|
59
|
+
console.log(` and all referenced policies — see \x1b[36m${apiBase}/api-terms\x1b[0m`);
|
|
60
|
+
console.log(" (run `openluxe terms`). Authorizing this device recorded");
|
|
61
|
+
console.log(" your acceptance; you'll be re-prompted only if they change.");
|
|
57
62
|
return;
|
|
58
63
|
}
|
|
59
64
|
if (poll.ok && poll.data?.status === 'pending') {
|
package/src/cli.js
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { request, ApiError } from './api.js';
|
|
2
2
|
import { RESOURCES } from './resources.js';
|
|
3
3
|
import * as auth from './auth.js';
|
|
4
|
-
import { load } from './config.js';
|
|
4
|
+
import { load, VERSION } from './config.js';
|
|
5
|
+
import { serve as mcpServe } from './mcp.js';
|
|
5
6
|
|
|
6
7
|
const C = {
|
|
7
8
|
dim: (s) => `\x1b[2m${s}\x1b[0m`,
|
|
@@ -53,6 +54,100 @@ function out(data) {
|
|
|
53
54
|
process.stdout.write(typeof data === 'string' ? data + '\n' : JSON.stringify(data, null, 2) + '\n');
|
|
54
55
|
}
|
|
55
56
|
|
|
57
|
+
/** The server hard-blocks every call until the API & CLI Terms are accepted. */
|
|
58
|
+
function isTermsBlock(e) {
|
|
59
|
+
return e?.status === 403
|
|
60
|
+
&& typeof e?.body?.type === 'string'
|
|
61
|
+
&& e.body.type.endsWith('/api_terms_required');
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/** The API/CLI/MCP are Pro surfaces — blocked until the user unlocks Pro access. */
|
|
65
|
+
function isPlatformAccessBlock(e) {
|
|
66
|
+
return e?.status === 402
|
|
67
|
+
&& ((typeof e?.body?.type === 'string' && e.body.type.endsWith('/platform_access_required'))
|
|
68
|
+
|| e?.body?.error === 'platform_access_required');
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function printPlatformAccessBlock(body = {}) {
|
|
72
|
+
const url = body.upgrade_url || body.landing_url || `${load().base}/pro`;
|
|
73
|
+
const price = typeof body.price_cents === 'number' ? `$${(body.price_cents / 100).toLocaleString()}` : null;
|
|
74
|
+
console.error(C.red('✗ Pro access required'));
|
|
75
|
+
console.error('');
|
|
76
|
+
console.error(' The OpenLuxe API, CLI, and MCP server are part of the Pro suite.');
|
|
77
|
+
console.error(` Unlock Pro access${price ? ` (${price} today)` : ''} to use them.`);
|
|
78
|
+
console.error('');
|
|
79
|
+
console.error(` Unlock here: ${C.cyan(url)}`);
|
|
80
|
+
console.error(C.dim(' Then re-run your command.'));
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
function printTermsBlock(body = {}) {
|
|
84
|
+
const acceptUrl = body.accept_url || `${load().base}/developers`;
|
|
85
|
+
console.error(C.red('✗ API & CLI Terms acceptance required'));
|
|
86
|
+
console.error('');
|
|
87
|
+
console.error(' Before using the API you must read and accept the current');
|
|
88
|
+
console.error(' OpenLuxe API & CLI Terms of Service and every referenced policy.');
|
|
89
|
+
console.error(' This is a one-time, in-browser step (it re-prompts only when');
|
|
90
|
+
console.error(' the terms change).');
|
|
91
|
+
console.error('');
|
|
92
|
+
console.error(` Sign in and accept here: ${C.cyan(acceptUrl)}`);
|
|
93
|
+
if (Array.isArray(body.required_policies) && body.required_policies.length) {
|
|
94
|
+
console.error('');
|
|
95
|
+
console.error(' You will be asked to accept:');
|
|
96
|
+
for (const p of body.required_policies) {
|
|
97
|
+
console.error(` • ${p.title}${p.url ? C.dim(' ' + p.url) : ''}`);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
console.error('');
|
|
101
|
+
console.error(C.dim(' Then re-run your command.'));
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/** A billable call hit a credit shortfall — surface the top-up path (no in-app modal here). */
|
|
105
|
+
function printInsufficientCredits(body = {}) {
|
|
106
|
+
const { base } = load();
|
|
107
|
+
const topup = body.topup_url || `${base}/credits/buy`;
|
|
108
|
+
console.error(C.red('✗ 402 Insufficient credits'));
|
|
109
|
+
if (body.message) console.error(` ${body.message}`);
|
|
110
|
+
if (body.required_credits !== undefined) {
|
|
111
|
+
console.error(C.dim(` required: ${body.required_credits} available: ${body.available_credits ?? 0} type: ${body.credit_type ?? 'credits'}`));
|
|
112
|
+
}
|
|
113
|
+
console.error('');
|
|
114
|
+
console.error(` Top up here: ${C.cyan(topup)}`);
|
|
115
|
+
console.error(C.dim(' Then re-run your command.'));
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/** `openluxe credits buy` — open / print the credit top-up page. */
|
|
119
|
+
function creditsBuy(flags = {}) {
|
|
120
|
+
const { base } = load();
|
|
121
|
+
const url = new URL(`${base.replace(/\/$/, '')}/credits/buy`);
|
|
122
|
+
if (flags.topup) url.searchParams.set('topup', flags.topup);
|
|
123
|
+
if (flags.required) url.searchParams.set('required', flags.required);
|
|
124
|
+
url.searchParams.set('label', flags.label || 'API credit top-up');
|
|
125
|
+
console.log(`Open this page to buy OpenLuxe credits:\n\n ${C.cyan(url.toString())}\n`);
|
|
126
|
+
console.log(C.dim('Credits fund AI-generating API calls (openluxe generate ...) at the same cost as the web app.'));
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
function termsHelp() {
|
|
130
|
+
const { base } = load();
|
|
131
|
+
console.log(`
|
|
132
|
+
${C.bold('openluxe terms')} — API & CLI Terms of Service
|
|
133
|
+
|
|
134
|
+
Using the OpenLuxe API or CLI is governed by a binding agreement.
|
|
135
|
+
You must read and accept it (and every referenced policy) once, in
|
|
136
|
+
your browser while signed in, before any request will work. You'll
|
|
137
|
+
be re-prompted only if the terms materially change.
|
|
138
|
+
|
|
139
|
+
Accept / review:
|
|
140
|
+
API & CLI Terms ${C.cyan(base + '/api-terms')}
|
|
141
|
+
Terms of Service ${C.cyan(base + '/terms-of-service')}
|
|
142
|
+
Privacy Policy ${C.cyan(base + '/privacy-policy')}
|
|
143
|
+
Acceptable Use ${C.cyan(base + '/acceptable-use')}
|
|
144
|
+
Data Policy ${C.cyan(base + '/data-policy')}
|
|
145
|
+
Cookie Policy ${C.cyan(base + '/cookie-policy')}
|
|
146
|
+
|
|
147
|
+
Accept from: ${C.cyan(base + '/developers')}
|
|
148
|
+
`);
|
|
149
|
+
}
|
|
150
|
+
|
|
56
151
|
async function callApi(method, path, { positionals = [], flags = {}, body }) {
|
|
57
152
|
// Fill :placeholders from --flag of the same name, else positionals in order.
|
|
58
153
|
const used = new Set();
|
|
@@ -80,6 +175,18 @@ async function callApi(method, path, { positionals = [], flags = {}, body }) {
|
|
|
80
175
|
out(res);
|
|
81
176
|
} catch (e) {
|
|
82
177
|
if (e instanceof ApiError) {
|
|
178
|
+
if (isTermsBlock(e)) {
|
|
179
|
+
printTermsBlock(e.body);
|
|
180
|
+
process.exit(1);
|
|
181
|
+
}
|
|
182
|
+
if (isPlatformAccessBlock(e)) {
|
|
183
|
+
printPlatformAccessBlock(e.body || {});
|
|
184
|
+
process.exit(1);
|
|
185
|
+
}
|
|
186
|
+
if (e.status === 402) {
|
|
187
|
+
printInsufficientCredits(e.body || {});
|
|
188
|
+
process.exit(1);
|
|
189
|
+
}
|
|
83
190
|
console.error(C.red(`✗ ${e.status} ${e.message}`));
|
|
84
191
|
if (e.body && typeof e.body === 'object') console.error(C.dim(JSON.stringify(e.body, null, 2)));
|
|
85
192
|
if (e.status === 401) console.error(C.dim(' Run: openluxe auth login'));
|
|
@@ -101,12 +208,19 @@ ${C.bold('AUTH')}
|
|
|
101
208
|
auth login Sign in via your browser (device flow)
|
|
102
209
|
auth logout Forget the local token
|
|
103
210
|
auth status Show who you're signed in as
|
|
211
|
+
terms Review / accept the API & CLI Terms
|
|
104
212
|
|
|
105
213
|
${C.bold('RAW')}
|
|
106
214
|
api <METHOD> <path> Call any v1 endpoint directly
|
|
107
215
|
e.g. openluxe api GET /contacts --per_page 5
|
|
108
216
|
openluxe api POST /notes -d '{"contact_id":1,"body":"hi"}'
|
|
109
217
|
|
|
218
|
+
${C.bold('AI / MCP')}
|
|
219
|
+
mcp Run an MCP server over stdio (point Claude Code,
|
|
220
|
+
Cursor, etc. at this to drive OpenLuxe in chat)
|
|
221
|
+
credits buy Open the credit top-up page (funds AI calls)
|
|
222
|
+
manifest Print the typed-command surface as JSON
|
|
223
|
+
|
|
110
224
|
${C.bold('RESOURCES')}
|
|
111
225
|
${Object.entries(RESOURCES).map(([g, r]) => ` ${g.padEnd(16)} ${C.dim(r.summary)}`).join('\n')}
|
|
112
226
|
|
|
@@ -131,7 +245,22 @@ export async function run(argv) {
|
|
|
131
245
|
const [cmd, ...rest] = argv;
|
|
132
246
|
|
|
133
247
|
if (!cmd || cmd === 'help' || cmd === '--help' || cmd === '-h') return topHelp();
|
|
134
|
-
if (cmd === '--version' || cmd === '-v') return out('openluxe
|
|
248
|
+
if (cmd === '--version' || cmd === '-v') return out('openluxe ' + VERSION);
|
|
249
|
+
if (cmd === 'terms') return termsHelp();
|
|
250
|
+
|
|
251
|
+
// MCP server over stdio — point Claude Code / Cursor at `openluxe mcp`.
|
|
252
|
+
if (cmd === 'mcp') return mcpServe();
|
|
253
|
+
|
|
254
|
+
// Emit the typed-command surface as JSON (feeds the coverage tooling).
|
|
255
|
+
if (cmd === 'manifest') {
|
|
256
|
+
const commands = [];
|
|
257
|
+
for (const [resource, def] of Object.entries(RESOURCES)) {
|
|
258
|
+
for (const [command, spec] of Object.entries(def.commands)) {
|
|
259
|
+
commands.push({ resource, command, method: spec.method, path: spec.path, summary: spec.summary || null });
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
return out({ version: VERSION, count: commands.length, commands });
|
|
263
|
+
}
|
|
135
264
|
|
|
136
265
|
if (cmd === 'auth') {
|
|
137
266
|
const sub = rest[0];
|
|
@@ -150,6 +279,12 @@ export async function run(argv) {
|
|
|
150
279
|
return callApi(method, path, { flags, body });
|
|
151
280
|
}
|
|
152
281
|
|
|
282
|
+
// `credits buy` opens the top-up page (not an API call).
|
|
283
|
+
if (cmd === 'credits' && rest[0] === 'buy') {
|
|
284
|
+
const { flags } = parseArgs(rest.slice(1));
|
|
285
|
+
return creditsBuy(flags);
|
|
286
|
+
}
|
|
287
|
+
|
|
153
288
|
const group = RESOURCES[cmd];
|
|
154
289
|
if (!group) return die(`Unknown command: ${cmd}. Run 'openluxe help'.`);
|
|
155
290
|
|
package/src/config.js
CHANGED
|
@@ -5,6 +5,11 @@ import { mkdirSync, readFileSync, writeFileSync, existsSync, chmodSync, rmSync }
|
|
|
5
5
|
const DIR = join(homedir(), '.openluxe');
|
|
6
6
|
const FILE = join(DIR, 'credentials.json');
|
|
7
7
|
|
|
8
|
+
/** Single source of truth for the CLI version — read from package.json. */
|
|
9
|
+
export const VERSION = JSON.parse(
|
|
10
|
+
readFileSync(new URL('../package.json', import.meta.url), 'utf8'),
|
|
11
|
+
).version;
|
|
12
|
+
|
|
8
13
|
const DEFAULT_BASE = process.env.OPENLUXE_API_URL || 'https://openluxe.co';
|
|
9
14
|
|
|
10
15
|
export function load() {
|
package/src/mcp.js
ADDED
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `openluxe mcp` — a zero-dependency MCP (Model Context Protocol) server over
|
|
3
|
+
* stdio that exposes the OpenLuxe v1 API to local AI clients (Claude Code,
|
|
4
|
+
* Cursor, etc.). It authenticates with the token stored by `openluxe auth login`
|
|
5
|
+
* and proxies every call to https://<base>/api/v1, so an agent gets the same
|
|
6
|
+
* scoped, ToS-gated, billed surface a human gets from the CLI.
|
|
7
|
+
*
|
|
8
|
+
* Transport: newline-delimited JSON-RPC 2.0 on stdin/stdout (the MCP stdio
|
|
9
|
+
* transport). We implement just enough of the protocol — initialize, tools/list,
|
|
10
|
+
* tools/call, ping — to be a useful client target.
|
|
11
|
+
*
|
|
12
|
+
* Tools are intentionally lean (a generic passthrough + endpoint discovery +
|
|
13
|
+
* a few high-value typed tools) rather than one-per-endpoint, so the agent's
|
|
14
|
+
* tool list stays small; `openluxe_api_request` covers the full ~300-endpoint
|
|
15
|
+
* surface. This mirrors the hosted /api/v1/mcp server.
|
|
16
|
+
*/
|
|
17
|
+
import { createInterface } from 'node:readline';
|
|
18
|
+
import { request, ApiError } from './api.js';
|
|
19
|
+
import { RESOURCES } from './resources.js';
|
|
20
|
+
import { load, VERSION } from './config.js';
|
|
21
|
+
|
|
22
|
+
const PROTOCOL_VERSION = '2025-06-18';
|
|
23
|
+
|
|
24
|
+
function send(msg) {
|
|
25
|
+
process.stdout.write(JSON.stringify(msg) + '\n');
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function result(id, result) {
|
|
29
|
+
send({ jsonrpc: '2.0', id, result });
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function error(id, code, message) {
|
|
33
|
+
send({ jsonrpc: '2.0', id, error: { code, message } });
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/** A flat catalog of every typed CLI command, for the discovery tool. */
|
|
37
|
+
function endpointCatalog() {
|
|
38
|
+
const rows = [];
|
|
39
|
+
for (const [resource, def] of Object.entries(RESOURCES)) {
|
|
40
|
+
for (const [command, spec] of Object.entries(def.commands)) {
|
|
41
|
+
rows.push({ resource, command, method: spec.method, path: spec.path, summary: spec.summary || null });
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
return rows;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
const TOOLS = [
|
|
48
|
+
{
|
|
49
|
+
name: 'openluxe_api_request',
|
|
50
|
+
description:
|
|
51
|
+
'Call any OpenLuxe v1 API endpoint. Use openluxe_list_endpoints first to discover paths. '
|
|
52
|
+
+ 'GET/DELETE use `query`; POST/PATCH/PUT use `body`. Paths are relative to /api/v1 (e.g. "/contacts").',
|
|
53
|
+
inputSchema: {
|
|
54
|
+
type: 'object',
|
|
55
|
+
properties: {
|
|
56
|
+
method: { type: 'string', enum: ['GET', 'POST', 'PATCH', 'PUT', 'DELETE'], description: 'HTTP method' },
|
|
57
|
+
path: { type: 'string', description: 'Path after /api/v1, e.g. /contacts or /contacts/42' },
|
|
58
|
+
query: { type: 'object', description: 'Query parameters (GET/DELETE)', additionalProperties: true },
|
|
59
|
+
body: { type: 'object', description: 'JSON body (POST/PATCH/PUT)', additionalProperties: true },
|
|
60
|
+
},
|
|
61
|
+
required: ['method', 'path'],
|
|
62
|
+
},
|
|
63
|
+
run: (args) => request(args.method, args.path, { query: args.query, body: args.body }),
|
|
64
|
+
},
|
|
65
|
+
{
|
|
66
|
+
name: 'openluxe_list_endpoints',
|
|
67
|
+
description: 'List every available OpenLuxe v1 endpoint (method, path, summary) grouped by resource, so you know what to pass to openluxe_api_request.',
|
|
68
|
+
inputSchema: {
|
|
69
|
+
type: 'object',
|
|
70
|
+
properties: { resource: { type: 'string', description: 'Optional: filter to one resource (e.g. "contacts")' } },
|
|
71
|
+
},
|
|
72
|
+
run: async (args) => {
|
|
73
|
+
const rows = endpointCatalog();
|
|
74
|
+
return args?.resource ? rows.filter((r) => r.resource === args.resource) : rows;
|
|
75
|
+
},
|
|
76
|
+
},
|
|
77
|
+
{
|
|
78
|
+
name: 'openluxe_me',
|
|
79
|
+
description: 'Show the authenticated user and the abilities (scopes) of the presenting token.',
|
|
80
|
+
inputSchema: { type: 'object', properties: {} },
|
|
81
|
+
run: () => request('GET', '/me'),
|
|
82
|
+
},
|
|
83
|
+
{
|
|
84
|
+
name: 'openluxe_search',
|
|
85
|
+
description: 'Universal search across the user\'s OpenLuxe records (contacts, listings, deals, etc.).',
|
|
86
|
+
inputSchema: { type: 'object', properties: { q: { type: 'string', description: 'Search query' } }, required: ['q'] },
|
|
87
|
+
run: (args) => request('GET', '/search', { query: { q: args.q } }),
|
|
88
|
+
},
|
|
89
|
+
{
|
|
90
|
+
name: 'openluxe_contacts_list',
|
|
91
|
+
description: 'List the user\'s CRM contacts. Optional filters: industry, email, phone, search, updated_since, created_via, per_page.',
|
|
92
|
+
inputSchema: { type: 'object', properties: { search: { type: 'string' }, industry: { type: 'string' }, per_page: { type: 'number' } } },
|
|
93
|
+
run: (args) => request('GET', '/contacts', { query: args || {} }),
|
|
94
|
+
},
|
|
95
|
+
{
|
|
96
|
+
name: 'openluxe_create_contact',
|
|
97
|
+
description: 'Create a CRM contact. Fields: email, first_name, last_name, phone, company, position, industry, etc.',
|
|
98
|
+
inputSchema: {
|
|
99
|
+
type: 'object',
|
|
100
|
+
properties: {
|
|
101
|
+
first_name: { type: 'string' }, last_name: { type: 'string' }, email: { type: 'string' },
|
|
102
|
+
phone: { type: 'string' }, company: { type: 'string' }, industry: { type: 'string' },
|
|
103
|
+
},
|
|
104
|
+
},
|
|
105
|
+
run: (args) => request('POST', '/contacts', { body: args || {} }),
|
|
106
|
+
},
|
|
107
|
+
{
|
|
108
|
+
name: 'openluxe_credits_balance',
|
|
109
|
+
description: 'Show the user\'s credit balance (what API generation calls are billed against).',
|
|
110
|
+
inputSchema: { type: 'object', properties: {} },
|
|
111
|
+
run: () => request('GET', '/credits/balance'),
|
|
112
|
+
},
|
|
113
|
+
];
|
|
114
|
+
|
|
115
|
+
const TOOL_INDEX = Object.fromEntries(TOOLS.map((t) => [t.name, t]));
|
|
116
|
+
|
|
117
|
+
async function handle(msg) {
|
|
118
|
+
const { id, method, params } = msg;
|
|
119
|
+
const isRequest = id !== undefined && id !== null;
|
|
120
|
+
|
|
121
|
+
switch (method) {
|
|
122
|
+
case 'initialize':
|
|
123
|
+
return result(id, {
|
|
124
|
+
protocolVersion: PROTOCOL_VERSION,
|
|
125
|
+
capabilities: { tools: {} },
|
|
126
|
+
serverInfo: { name: 'openluxe', version: VERSION },
|
|
127
|
+
instructions: 'Drive the OpenLuxe v1 API (CRM, listings, business, generation, …). '
|
|
128
|
+
+ 'Start with openluxe_list_endpoints, then openluxe_api_request for anything not covered by a typed tool.',
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
case 'notifications/initialized':
|
|
132
|
+
case 'notifications/cancelled':
|
|
133
|
+
return; // notifications get no response
|
|
134
|
+
|
|
135
|
+
case 'ping':
|
|
136
|
+
return isRequest && result(id, {});
|
|
137
|
+
|
|
138
|
+
case 'tools/list':
|
|
139
|
+
return result(id, {
|
|
140
|
+
tools: TOOLS.map(({ name, description, inputSchema }) => ({ name, description, inputSchema })),
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
case 'tools/call': {
|
|
144
|
+
const tool = TOOL_INDEX[params?.name];
|
|
145
|
+
if (!tool) return error(id, -32602, `Unknown tool: ${params?.name}`);
|
|
146
|
+
try {
|
|
147
|
+
const data = await tool.run(params.arguments || {});
|
|
148
|
+
return result(id, { content: [{ type: 'text', text: JSON.stringify(data, null, 2) }] });
|
|
149
|
+
} catch (e) {
|
|
150
|
+
// Surface API errors to the model as tool errors (isError), not protocol errors.
|
|
151
|
+
const text = e instanceof ApiError
|
|
152
|
+
? `API error ${e.status}: ${e.message}\n${JSON.stringify(e.body ?? {}, null, 2)}`
|
|
153
|
+
: `Error: ${e.message}`;
|
|
154
|
+
return result(id, { content: [{ type: 'text', text }], isError: true });
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
default:
|
|
159
|
+
return isRequest && error(id, -32601, `Method not found: ${method}`);
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
export async function serve() {
|
|
164
|
+
const { token, base } = load();
|
|
165
|
+
if (!token) {
|
|
166
|
+
process.stderr.write('openluxe mcp: not signed in — run `openluxe auth login` first.\n');
|
|
167
|
+
process.exit(1);
|
|
168
|
+
}
|
|
169
|
+
process.stderr.write(`openluxe mcp server ready (${base}) — ${TOOLS.length} tools.\n`);
|
|
170
|
+
|
|
171
|
+
const rl = createInterface({ input: process.stdin });
|
|
172
|
+
for await (const line of rl) {
|
|
173
|
+
const trimmed = line.trim();
|
|
174
|
+
if (!trimmed) continue;
|
|
175
|
+
let msg;
|
|
176
|
+
try {
|
|
177
|
+
msg = JSON.parse(trimmed);
|
|
178
|
+
} catch {
|
|
179
|
+
error(null, -32700, 'Parse error');
|
|
180
|
+
continue;
|
|
181
|
+
}
|
|
182
|
+
try {
|
|
183
|
+
await handle(msg);
|
|
184
|
+
} catch (e) {
|
|
185
|
+
if (msg?.id !== undefined && msg?.id !== null) error(msg.id, -32603, `Internal error: ${e.message}`);
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
}
|
package/src/resources.js
CHANGED
|
@@ -8,7 +8,9 @@
|
|
|
8
8
|
* - For POST/PATCH: --flags + `-d '<json>'` merge into the JSON body.
|
|
9
9
|
*
|
|
10
10
|
* This is intentionally data-driven so the CLI mirrors the API surface
|
|
11
|
-
* without 100+ hand-written command files.
|
|
11
|
+
* without 100+ hand-written command files. Curated resources are kept
|
|
12
|
+
* verbatim; the rest are generated for full parity with the v1 route table
|
|
13
|
+
* (regenerate via the main repo's scripts + `openluxe manifest`).
|
|
12
14
|
*/
|
|
13
15
|
export const RESOURCES = {
|
|
14
16
|
contacts: {
|
|
@@ -19,6 +21,46 @@ export const RESOURCES = {
|
|
|
19
21
|
create: { method: 'POST', path: '/contacts', summary: 'Create a contact' },
|
|
20
22
|
update: { method: 'PATCH', path: '/contacts/:contact', summary: 'Update a contact' },
|
|
21
23
|
delete: { method: 'DELETE', path: '/contacts/:contact', summary: 'Delete a contact' },
|
|
24
|
+
problems: { method: 'GET', path: '/contacts/:contact/problems', summary: 'GET /contacts/{contact}/problems [scope: crm:contacts:read]' },
|
|
25
|
+
'cultural-briefing': { method: 'GET', path: '/contacts/:contact/cultural-briefing', summary: 'GET /contacts/{contact}/cultural-briefing [scope: crm:cultural:read]' },
|
|
26
|
+
personality: { method: 'GET', path: '/contacts/:contact/personality', summary: 'GET /contacts/{contact}/personality [scope: crm:contacts:read]' },
|
|
27
|
+
favors: { method: 'GET', path: '/contacts/:contact/favors', summary: 'GET /contacts/{contact}/favors [scope: crm:contacts:read]' },
|
|
28
|
+
'compliance-limits': { method: 'GET', path: '/contacts/:contact/compliance-limits', summary: 'GET /contacts/{contact}/compliance-limits [scope: crm:contacts:read]' },
|
|
29
|
+
status: { method: 'GET', path: '/contacts/:contact/compliance-limits/status', summary: 'GET /contacts/{contact}/compliance-limits/status [scope: crm:contacts:read]' },
|
|
30
|
+
value: { method: 'GET', path: '/contacts/:contact/value', summary: 'GET /contacts/{contact}/value [scope: crm:contacts:read]' },
|
|
31
|
+
economics: { method: 'GET', path: '/contacts/:contact/economics', summary: 'GET /contacts/{contact}/economics [scope: crm:contacts:read]' },
|
|
32
|
+
'buyer-profile': { method: 'GET', path: '/contacts/:contact/buyer-profile', summary: 'GET /contacts/{contact}/buyer-profile [scope: crm:contacts:read]' },
|
|
33
|
+
'user-links': { method: 'GET', path: '/contacts/:contact/user-links', summary: 'GET /contacts/{contact}/user-links [scope: crm:contacts:read]' },
|
|
34
|
+
'update-personality': { method: 'PUT', path: '/contacts/:contact/personality', summary: 'PUT /contacts/{contact}/personality [scope: crm:contacts:write]' },
|
|
35
|
+
'create-favors': { method: 'POST', path: '/contacts/:contact/favors', summary: 'POST /contacts/{contact}/favors [scope: crm:contacts:write]' },
|
|
36
|
+
'update-favors': { method: 'PUT', path: '/contacts/:contact/favors/:favor', summary: 'PUT /contacts/{contact}/favors/{favor} [scope: crm:contacts:write]' },
|
|
37
|
+
'delete-favors': { method: 'DELETE', path: '/contacts/:contact/favors/:favor', summary: 'DELETE /contacts/{contact}/favors/{favor} [scope: crm:contacts:write]' },
|
|
38
|
+
reciprocate: { method: 'POST', path: '/contacts/:contact/favors/:favor/reciprocate', summary: 'POST /contacts/{contact}/favors/{favor}/reciprocate [scope: crm:contacts:write]' },
|
|
39
|
+
'create-compliance-limits': { method: 'POST', path: '/contacts/:contact/compliance-limits', summary: 'POST /contacts/{contact}/compliance-limits [scope: crm:contacts:write]' },
|
|
40
|
+
'update-compliance-limits': { method: 'PUT', path: '/contacts/:contact/compliance-limits/:limit', summary: 'PUT /contacts/{contact}/compliance-limits/{limit} [scope: crm:contacts:write]' },
|
|
41
|
+
'delete-compliance-limits': { method: 'DELETE', path: '/contacts/:contact/compliance-limits/:limit', summary: 'DELETE /contacts/{contact}/compliance-limits/{limit} [scope: crm:contacts:write]' },
|
|
42
|
+
issue: { method: 'POST', path: '/contacts/:contact/compliance-override/issue', summary: 'POST /contacts/{contact}/compliance-override/issue [scope: crm:contacts:write]' },
|
|
43
|
+
'create-value-manual-entry': { method: 'POST', path: '/contacts/:contact/value/manual-entry', summary: 'POST /contacts/{contact}/value/manual-entry [scope: crm:contacts:write]' },
|
|
44
|
+
'update-value-expected-ltv': { method: 'PUT', path: '/contacts/:contact/value/expected-ltv', summary: 'PUT /contacts/{contact}/value/expected-ltv [scope: crm:contacts:write]' },
|
|
45
|
+
'create-user-links': { method: 'POST', path: '/contacts/:contact/user-links', summary: 'POST /contacts/{contact}/user-links [scope: crm:contacts:write]' },
|
|
46
|
+
discover: { method: 'POST', path: '/contacts/:contact/user-links/discover', summary: 'POST /contacts/{contact}/user-links/discover [scope: crm:contacts:write]' },
|
|
47
|
+
confirm: { method: 'POST', path: '/contacts/:contact/user-links/:link/confirm', summary: 'POST /contacts/{contact}/user-links/{link}/confirm [scope: crm:contacts:write]' },
|
|
48
|
+
reject: { method: 'POST', path: '/contacts/:contact/user-links/:link/reject', summary: 'POST /contacts/{contact}/user-links/{link}/reject [scope: crm:contacts:write]' },
|
|
49
|
+
overview: { method: 'GET', path: '/contacts/:contact/owned-assets/:asset/overview', summary: 'GET /contacts/{contact}/owned-assets/{asset}/overview [scope: crm:assets:read]' },
|
|
50
|
+
'owned-assets-service-events': { method: 'GET', path: '/contacts/:contact/owned-assets/:asset/service-events', summary: 'GET /contacts/{contact}/owned-assets/{asset}/service-events [scope: crm:assets:read]' },
|
|
51
|
+
'owned-assets-locations': { method: 'GET', path: '/contacts/:contact/owned-assets/:asset/locations', summary: 'GET /contacts/{contact}/owned-assets/{asset}/locations [scope: crm:assets:read]' },
|
|
52
|
+
'owned-assets-upgrades': { method: 'GET', path: '/contacts/:contact/owned-assets/:asset/upgrades', summary: 'GET /contacts/{contact}/owned-assets/{asset}/upgrades [scope: crm:assets:read]' },
|
|
53
|
+
'create-owned-assets-service-events': { method: 'POST', path: '/contacts/:contact/owned-assets/:asset/service-events', summary: 'POST /contacts/{contact}/owned-assets/{asset}/service-events [scope: crm:assets:write]' },
|
|
54
|
+
'update-owned-assets-service-events': { method: 'PUT', path: '/contacts/:contact/owned-assets/:asset/service-events/:event', summary: 'PUT /contacts/{contact}/owned-assets/{asset}/service-events/{event} [scope: crm:assets:write]' },
|
|
55
|
+
'delete-owned-assets-service-events': { method: 'DELETE', path: '/contacts/:contact/owned-assets/:asset/service-events/:event', summary: 'DELETE /contacts/{contact}/owned-assets/{asset}/service-events/{event} [scope: crm:assets:write]' },
|
|
56
|
+
'create-owned-assets-locations': { method: 'POST', path: '/contacts/:contact/owned-assets/:asset/locations', summary: 'POST /contacts/{contact}/owned-assets/{asset}/locations [scope: crm:assets:write]' },
|
|
57
|
+
'create-owned-assets-upgrades': { method: 'POST', path: '/contacts/:contact/owned-assets/:asset/upgrades', summary: 'POST /contacts/{contact}/owned-assets/{asset}/upgrades [scope: crm:assets:write]' },
|
|
58
|
+
'commission-partnerships': { method: 'GET', path: '/contacts/:contact/commission-partnerships', summary: 'GET /contacts/{contact}/commission-partnerships [scope: crm:commission:read]' },
|
|
59
|
+
earnings: { method: 'GET', path: '/contacts/:contact/commission-partnerships/:partnership/earnings', summary: 'GET /contacts/{contact}/commission-partnerships/{partnership}/earnings [scope: crm:commission:read]' },
|
|
60
|
+
'create-commission-partnerships': { method: 'POST', path: '/contacts/:contact/commission-partnerships', summary: 'POST /contacts/{contact}/commission-partnerships [scope: crm:commission:write]' },
|
|
61
|
+
'create-earnings': { method: 'POST', path: '/contacts/:contact/commission-partnerships/:partnership/earnings', summary: 'POST /contacts/{contact}/commission-partnerships/{partnership}/earnings [scope: crm:commission:write]' },
|
|
62
|
+
'update-commission-partnerships': { method: 'PUT', path: '/contacts/:contact/commission-partnerships/:partnership', summary: 'PUT /contacts/{contact}/commission-partnerships/{partnership} [scope: crm:commission:write]' },
|
|
63
|
+
'delete-commission-partnerships': { method: 'DELETE', path: '/contacts/:contact/commission-partnerships/:partnership', summary: 'DELETE /contacts/{contact}/commission-partnerships/{partnership} [scope: crm:commission:write]' },
|
|
22
64
|
},
|
|
23
65
|
},
|
|
24
66
|
notes: {
|
|
@@ -66,6 +108,8 @@ export const RESOURCES = {
|
|
|
66
108
|
create: { method: 'POST', path: '/contact-lists' },
|
|
67
109
|
update: { method: 'PATCH', path: '/contact-lists/:list' },
|
|
68
110
|
delete: { method: 'DELETE', path: '/contact-lists/:list' },
|
|
111
|
+
'create-contacts': { method: 'POST', path: '/contact-lists/:list/contacts', summary: 'POST /contact-lists/{list}/contacts [scope: crm:lists:write]' },
|
|
112
|
+
'delete-contacts': { method: 'DELETE', path: '/contact-lists/:list/contacts', summary: 'DELETE /contact-lists/{list}/contacts [scope: crm:lists:write]' },
|
|
69
113
|
},
|
|
70
114
|
},
|
|
71
115
|
listings: {
|
|
@@ -134,11 +178,20 @@ export const RESOURCES = {
|
|
|
134
178
|
enrollments: { method: 'GET', path: '/course-enrollments' },
|
|
135
179
|
},
|
|
136
180
|
},
|
|
181
|
+
smartboards: {
|
|
182
|
+
summary: 'Smartboards / collaborative diagrams (read-only in v1)',
|
|
183
|
+
commands: {
|
|
184
|
+
list: { method: 'GET', path: '/smartboards' },
|
|
185
|
+
get: { method: 'GET', path: '/smartboards/:smartboard', summary: 'One board with its elements' },
|
|
186
|
+
},
|
|
187
|
+
},
|
|
137
188
|
credits: {
|
|
138
189
|
summary: 'Credit balance & ledger',
|
|
139
190
|
commands: {
|
|
140
191
|
balance: { method: 'GET', path: '/credits/balance' },
|
|
141
192
|
ledger: { method: 'GET', path: '/credits/ledger' },
|
|
193
|
+
pricing: { method: 'GET', path: '/credits/pricing', summary: 'GET /credits/pricing [scope: credits:balance:read]' },
|
|
194
|
+
usage: { method: 'GET', path: '/credits/usage', summary: 'GET /credits/usage [scope: credits:balance:read]' },
|
|
142
195
|
},
|
|
143
196
|
},
|
|
144
197
|
webhooks: {
|
|
@@ -178,4 +231,544 @@ export const RESOURCES = {
|
|
|
178
231
|
show: { method: 'GET', path: '/me', summary: 'Who am I + token scopes' },
|
|
179
232
|
},
|
|
180
233
|
},
|
|
234
|
+
generate: {
|
|
235
|
+
summary: 'AI generation (async): start a job, then poll with `generations get <id>`',
|
|
236
|
+
commands: {
|
|
237
|
+
start: { method: 'POST', path: '/generate/:feature', summary: 'Start a generation. feature ∈ image, video, sound_effect, email_template, sales_presentation, blog_article, podcast, sticker, brand_colors, dossier, agent_odysseus, agent_apollo, agent_atticus, agent_eva, agent_alfred. Inputs via -d \'<json>\'. Returns a 202 handle.' },
|
|
238
|
+
},
|
|
239
|
+
},
|
|
240
|
+
generations: {
|
|
241
|
+
summary: 'Poll an async AI generation handle',
|
|
242
|
+
commands: {
|
|
243
|
+
get: { method: 'GET', path: '/generations/:id', summary: 'Poll a generation by id — status (queued|processing|succeeded|failed) + result' },
|
|
244
|
+
},
|
|
245
|
+
},
|
|
246
|
+
cli: {
|
|
247
|
+
summary: 'cli (v1)',
|
|
248
|
+
commands: {
|
|
249
|
+
'create-auth-start': { method: 'POST', path: '/cli/auth/start', summary: 'POST /cli/auth/start' },
|
|
250
|
+
'create-auth-poll': { method: 'POST', path: '/cli/auth/poll', summary: 'POST /cli/auth/poll' },
|
|
251
|
+
},
|
|
252
|
+
},
|
|
253
|
+
industries: {
|
|
254
|
+
summary: 'industries (v1)',
|
|
255
|
+
commands: {
|
|
256
|
+
list: { method: 'GET', path: '/industries', summary: 'GET /industries' },
|
|
257
|
+
schema: { method: 'GET', path: '/industries/:industry/schema', summary: 'GET /industries/{industry}/schema' },
|
|
258
|
+
},
|
|
259
|
+
},
|
|
260
|
+
'live-shop': {
|
|
261
|
+
summary: 'live shop (v1)',
|
|
262
|
+
commands: {
|
|
263
|
+
list: { method: 'GET', path: '/live-shop', summary: 'GET /live-shop [scope: livestreams:read]' },
|
|
264
|
+
},
|
|
265
|
+
},
|
|
266
|
+
kanbans: {
|
|
267
|
+
summary: 'kanbans (v1)',
|
|
268
|
+
commands: {
|
|
269
|
+
get: { method: 'GET', path: '/kanbans/:kanban', summary: 'GET /kanbans/{kanban} [scope: crm:kanban:read]' },
|
|
270
|
+
list: { method: 'GET', path: '/kanbans', summary: 'GET /kanbans [scope: crm:kanban:read]' },
|
|
271
|
+
lists: { method: 'GET', path: '/kanbans/:kanban/lists', summary: 'GET /kanbans/{kanban}/lists [scope: crm:kanban:read]' },
|
|
272
|
+
},
|
|
273
|
+
},
|
|
274
|
+
'business-org-chart': {
|
|
275
|
+
summary: 'business org chart (v1)',
|
|
276
|
+
commands: {
|
|
277
|
+
list: { method: 'GET', path: '/business/org-chart', summary: 'GET /business/org-chart [scope: business:org:read]' },
|
|
278
|
+
},
|
|
279
|
+
},
|
|
280
|
+
'business-departments': {
|
|
281
|
+
summary: 'business departments (v1)',
|
|
282
|
+
commands: {
|
|
283
|
+
list: { method: 'GET', path: '/business/departments', summary: 'GET /business/departments [scope: business:org:read]' },
|
|
284
|
+
},
|
|
285
|
+
},
|
|
286
|
+
'crm-cultural-library': {
|
|
287
|
+
summary: 'crm cultural library (v1)',
|
|
288
|
+
commands: {
|
|
289
|
+
list: { method: 'GET', path: '/crm/cultural-library', summary: 'GET /crm/cultural-library [scope: crm:cultural:read]' },
|
|
290
|
+
get: { method: 'GET', path: '/crm/cultural-library/:profile', summary: 'GET /crm/cultural-library/{profile} [scope: crm:cultural:read]' },
|
|
291
|
+
},
|
|
292
|
+
},
|
|
293
|
+
'crm-portfolio': {
|
|
294
|
+
summary: 'crm portfolio (v1)',
|
|
295
|
+
commands: {
|
|
296
|
+
get: { method: 'GET', path: '/crm/portfolio/:section', summary: 'GET /crm/portfolio/{section} [scope: crm:portfolio:read]' },
|
|
297
|
+
},
|
|
298
|
+
},
|
|
299
|
+
'client-deals': {
|
|
300
|
+
summary: 'client deals (v1)',
|
|
301
|
+
commands: {
|
|
302
|
+
list: { method: 'GET', path: '/client-deals', summary: 'GET /client-deals [scope: crm:client_deals:read]' },
|
|
303
|
+
get: { method: 'GET', path: '/client-deals/:uuid', summary: 'GET /client-deals/{uuid} [scope: crm:client_deals:read]' },
|
|
304
|
+
},
|
|
305
|
+
},
|
|
306
|
+
invoices: {
|
|
307
|
+
summary: 'invoices (v1)',
|
|
308
|
+
commands: {
|
|
309
|
+
list: { method: 'GET', path: '/invoices', summary: 'GET /invoices [scope: crm:invoices:read]' },
|
|
310
|
+
get: { method: 'GET', path: '/invoices/:invoice', summary: 'GET /invoices/{invoice} [scope: crm:invoices:read]' },
|
|
311
|
+
},
|
|
312
|
+
},
|
|
313
|
+
reviews: {
|
|
314
|
+
summary: 'reviews (v1)',
|
|
315
|
+
commands: {
|
|
316
|
+
list: { method: 'GET', path: '/reviews', summary: 'GET /reviews [scope: crm:reviews:read]' },
|
|
317
|
+
get: { method: 'GET', path: '/reviews/:review', summary: 'GET /reviews/{review} [scope: crm:reviews:read]' },
|
|
318
|
+
},
|
|
319
|
+
},
|
|
320
|
+
'message-campaigns': {
|
|
321
|
+
summary: 'message campaigns (v1)',
|
|
322
|
+
commands: {
|
|
323
|
+
list: { method: 'GET', path: '/message-campaigns', summary: 'GET /message-campaigns [scope: crm:campaigns:read]' },
|
|
324
|
+
get: { method: 'GET', path: '/message-campaigns/:uuid', summary: 'GET /message-campaigns/{uuid} [scope: crm:campaigns:read]' },
|
|
325
|
+
create: { method: 'POST', path: '/message-campaigns', summary: 'POST /message-campaigns [scope: crm:campaigns:write]' },
|
|
326
|
+
enroll: { method: 'POST', path: '/message-campaigns/:uuid/enroll', summary: 'POST /message-campaigns/{uuid}/enroll [scope: crm:campaigns:write]' },
|
|
327
|
+
},
|
|
328
|
+
},
|
|
329
|
+
'phone-numbers': {
|
|
330
|
+
summary: 'phone numbers (v1)',
|
|
331
|
+
commands: {
|
|
332
|
+
list: { method: 'GET', path: '/phone-numbers', summary: 'GET /phone-numbers [scope: comms:numbers:read]' },
|
|
333
|
+
'list-2': { method: 'GET', path: '/phone-numbers/:uuid', summary: 'GET /phone-numbers/{uuid} [scope: comms:numbers:read]' },
|
|
334
|
+
create: { method: 'POST', path: '/phone-numbers', summary: 'POST /phone-numbers [scope: comms:numbers:write]' },
|
|
335
|
+
},
|
|
336
|
+
},
|
|
337
|
+
'command-centers': {
|
|
338
|
+
summary: 'command centers (v1)',
|
|
339
|
+
commands: {
|
|
340
|
+
overview: { method: 'GET', path: '/command-centers/:slug/overview', summary: 'GET /command-centers/{slug}/overview [scope: business:command-center:read]' },
|
|
341
|
+
missions: { method: 'GET', path: '/command-centers/:slug/missions', summary: 'GET /command-centers/{slug}/missions [scope: business:command-center:read]' },
|
|
342
|
+
'missions-2': { method: 'GET', path: '/command-centers/:slug/missions/:mission', summary: 'GET /command-centers/{slug}/missions/{mission} [scope: business:command-center:read]' },
|
|
343
|
+
'work-feed': { method: 'GET', path: '/command-centers/:slug/work-feed', summary: 'GET /command-centers/{slug}/work-feed [scope: business:command-center:read]' },
|
|
344
|
+
'performance-competencies': { method: 'GET', path: '/command-centers/:slug/performance/competencies', summary: 'GET /command-centers/{slug}/performance/competencies [scope: business:performance:read]' },
|
|
345
|
+
'performance-templates': { method: 'GET', path: '/command-centers/:slug/performance/templates', summary: 'GET /command-centers/{slug}/performance/templates [scope: business:performance:read]' },
|
|
346
|
+
reviews: { method: 'GET', path: '/command-centers/:slug/performance/reviews', summary: 'GET /command-centers/{slug}/performance/reviews [scope: business:performance:read]' },
|
|
347
|
+
'reviews-2': { method: 'GET', path: '/command-centers/:slug/performance/reviews/:review', summary: 'GET /command-centers/{slug}/performance/reviews/{review} [scope: business:performance:read]' },
|
|
348
|
+
'performance-subjects-rollup': { method: 'GET', path: '/command-centers/:slug/performance/subjects/:subject/rollup', summary: 'GET /command-centers/{slug}/performance/subjects/{subject}/rollup [scope: business:performance:read]' },
|
|
349
|
+
'performance-plans': { method: 'GET', path: '/command-centers/:slug/performance/plans', summary: 'GET /command-centers/{slug}/performance/plans [scope: business:performance:read]' },
|
|
350
|
+
},
|
|
351
|
+
},
|
|
352
|
+
'email-messages': {
|
|
353
|
+
summary: 'email messages (v1)',
|
|
354
|
+
commands: {
|
|
355
|
+
list: { method: 'GET', path: '/email-messages', summary: 'GET /email-messages [scope: comms:email:read]' },
|
|
356
|
+
get: { method: 'GET', path: '/email-messages/:emailMessage', summary: 'GET /email-messages/{emailMessage} [scope: comms:email:read]' },
|
|
357
|
+
send: { method: 'POST', path: '/email-messages/send', summary: 'POST /email-messages/send [scope: comms:email:send]' },
|
|
358
|
+
},
|
|
359
|
+
},
|
|
360
|
+
'email-deliverability-contact-lists': {
|
|
361
|
+
summary: 'email deliverability contact lists (v1)',
|
|
362
|
+
commands: {
|
|
363
|
+
status: { method: 'GET', path: '/email-deliverability/contact-lists/:contactList/status', summary: 'GET /email-deliverability/contact-lists/{contactList}/status [scope: comms:email:deliverability:read]' },
|
|
364
|
+
validate: { method: 'POST', path: '/email-deliverability/contact-lists/:contactList/validate', summary: 'POST /email-deliverability/contact-lists/{contactList}/validate [scope: comms:email:deliverability:write]' },
|
|
365
|
+
},
|
|
366
|
+
},
|
|
367
|
+
'email-suppression': {
|
|
368
|
+
summary: 'email suppression (v1)',
|
|
369
|
+
commands: {
|
|
370
|
+
list: { method: 'GET', path: '/email-suppression', summary: 'GET /email-suppression [scope: comms:email:suppression:read]' },
|
|
371
|
+
},
|
|
372
|
+
},
|
|
373
|
+
'sms-messages': {
|
|
374
|
+
summary: 'sms messages (v1)',
|
|
375
|
+
commands: {
|
|
376
|
+
list: { method: 'GET', path: '/sms-messages', summary: 'GET /sms-messages [scope: comms:sms:read]' },
|
|
377
|
+
get: { method: 'GET', path: '/sms-messages/:smsMessage', summary: 'GET /sms-messages/{smsMessage} [scope: comms:sms:read]' },
|
|
378
|
+
send: { method: 'POST', path: '/sms-messages/send', summary: 'POST /sms-messages/send [scope: comms:sms:write]' },
|
|
379
|
+
},
|
|
380
|
+
},
|
|
381
|
+
calls: {
|
|
382
|
+
summary: 'calls (v1)',
|
|
383
|
+
commands: {
|
|
384
|
+
list: { method: 'GET', path: '/calls', summary: 'GET /calls [scope: comms:phone:read]' },
|
|
385
|
+
get: { method: 'GET', path: '/calls/:call', summary: 'GET /calls/{call} [scope: comms:phone:read]' },
|
|
386
|
+
},
|
|
387
|
+
},
|
|
388
|
+
'kanban-cards': {
|
|
389
|
+
summary: 'kanban cards (v1)',
|
|
390
|
+
commands: {
|
|
391
|
+
list: { method: 'GET', path: '/kanban-cards', summary: 'GET /kanban-cards [scope: crm:kanban:read]' },
|
|
392
|
+
},
|
|
393
|
+
},
|
|
394
|
+
'email-templates': {
|
|
395
|
+
summary: 'email templates (v1)',
|
|
396
|
+
commands: {
|
|
397
|
+
list: { method: 'GET', path: '/email-templates', summary: 'GET /email-templates [scope: comms:email-templates:read]' },
|
|
398
|
+
get: { method: 'GET', path: '/email-templates/:slug', summary: 'GET /email-templates/{slug} [scope: comms:email-templates:read]' },
|
|
399
|
+
},
|
|
400
|
+
},
|
|
401
|
+
'mini-games': {
|
|
402
|
+
summary: 'mini games (v1)',
|
|
403
|
+
commands: {
|
|
404
|
+
plays: { method: 'GET', path: '/mini-games/plays', summary: 'GET /mini-games/plays [scope: games:mini-games:read]' },
|
|
405
|
+
},
|
|
406
|
+
},
|
|
407
|
+
arena: {
|
|
408
|
+
summary: 'arena (v1)',
|
|
409
|
+
commands: {
|
|
410
|
+
matches: { method: 'GET', path: '/arena/matches', summary: 'GET /arena/matches [scope: games:arena:read]' },
|
|
411
|
+
},
|
|
412
|
+
},
|
|
413
|
+
nft: {
|
|
414
|
+
summary: 'nft (v1)',
|
|
415
|
+
commands: {
|
|
416
|
+
collections: { method: 'GET', path: '/nft/collections', summary: 'GET /nft/collections [scope: nft:collections:read]' },
|
|
417
|
+
assets: { method: 'GET', path: '/nft/assets', summary: 'GET /nft/assets [scope: nft:assets:read]' },
|
|
418
|
+
'collections-2': { method: 'GET', path: '/nft/collections/:collection', summary: 'GET /nft/collections/{collection} [scope: nft:collections:read]' },
|
|
419
|
+
'assets-2': { method: 'GET', path: '/nft/assets/:asset', summary: 'GET /nft/assets/{asset} [scope: nft:assets:read]' },
|
|
420
|
+
},
|
|
421
|
+
},
|
|
422
|
+
openflix: {
|
|
423
|
+
summary: 'openflix (v1)',
|
|
424
|
+
commands: {
|
|
425
|
+
movies: { method: 'GET', path: '/openflix/movies', summary: 'GET /openflix/movies [scope: media:openflix:read]' },
|
|
426
|
+
series: { method: 'GET', path: '/openflix/series', summary: 'GET /openflix/series [scope: media:openflix:read]' },
|
|
427
|
+
'movies-2': { method: 'GET', path: '/openflix/movies/:movie', summary: 'GET /openflix/movies/{movie} [scope: media:openflix:read]' },
|
|
428
|
+
'series-2': { method: 'GET', path: '/openflix/series/:series', summary: 'GET /openflix/series/{series} [scope: media:openflix:read]' },
|
|
429
|
+
},
|
|
430
|
+
},
|
|
431
|
+
livestreams: {
|
|
432
|
+
summary: 'livestreams (v1)',
|
|
433
|
+
commands: {
|
|
434
|
+
list: { method: 'GET', path: '/livestreams', summary: 'GET /livestreams [scope: media:livestreams:read]' },
|
|
435
|
+
get: { method: 'GET', path: '/livestreams/:room', summary: 'GET /livestreams/{room} [scope: media:livestreams:read]' },
|
|
436
|
+
},
|
|
437
|
+
},
|
|
438
|
+
profile: {
|
|
439
|
+
summary: 'profile (v1)',
|
|
440
|
+
commands: {
|
|
441
|
+
list: { method: 'GET', path: '/profile', summary: 'GET /profile [scope: profile:read]' },
|
|
442
|
+
},
|
|
443
|
+
},
|
|
444
|
+
affiliate: {
|
|
445
|
+
summary: 'affiliate (v1)',
|
|
446
|
+
commands: {
|
|
447
|
+
program: { method: 'GET', path: '/affiliate/program', summary: 'GET /affiliate/program [scope: affiliate:program:read]' },
|
|
448
|
+
commissions: { method: 'GET', path: '/affiliate/commissions', summary: 'GET /affiliate/commissions [scope: affiliate:program:read]' },
|
|
449
|
+
payouts: { method: 'GET', path: '/affiliate/payouts', summary: 'GET /affiliate/payouts [scope: affiliate:payouts:read]' },
|
|
450
|
+
},
|
|
451
|
+
},
|
|
452
|
+
print: {
|
|
453
|
+
summary: 'print (v1)',
|
|
454
|
+
commands: {
|
|
455
|
+
'minimum-quantities': { method: 'GET', path: '/print/minimum-quantities', summary: 'GET /print/minimum-quantities [scope: print:fulfillment:read]' },
|
|
456
|
+
'minimum-quantities-2': { method: 'GET', path: '/print/minimum-quantities/:productTypeId', summary: 'GET /print/minimum-quantities/{productTypeId} [scope: print:fulfillment:read]' },
|
|
457
|
+
},
|
|
458
|
+
},
|
|
459
|
+
fulfillment: {
|
|
460
|
+
summary: 'fulfillment (v1)',
|
|
461
|
+
commands: {
|
|
462
|
+
queue: { method: 'GET', path: '/fulfillment/queue', summary: 'GET /fulfillment/queue [scope: fulfillment:read]' },
|
|
463
|
+
analytics: { method: 'GET', path: '/fulfillment/analytics', summary: 'GET /fulfillment/analytics [scope: fulfillment:read]' },
|
|
464
|
+
},
|
|
465
|
+
},
|
|
466
|
+
'branding-projects': {
|
|
467
|
+
summary: 'branding projects (v1)',
|
|
468
|
+
commands: {
|
|
469
|
+
list: { method: 'GET', path: '/branding-projects', summary: 'GET /branding-projects [scope: brand:projects:read]' },
|
|
470
|
+
get: { method: 'GET', path: '/branding-projects/:project', summary: 'GET /branding-projects/{project} [scope: brand:projects:read]' },
|
|
471
|
+
},
|
|
472
|
+
},
|
|
473
|
+
brands: {
|
|
474
|
+
summary: 'brands (v1)',
|
|
475
|
+
commands: {
|
|
476
|
+
list: { method: 'GET', path: '/brands', summary: 'GET /brands [scope: brand:assets:read]' },
|
|
477
|
+
get: { method: 'GET', path: '/brands/:brand', summary: 'GET /brands/{brand} [scope: brand:assets:read]' },
|
|
478
|
+
},
|
|
479
|
+
},
|
|
480
|
+
associations: {
|
|
481
|
+
summary: 'associations (v1)',
|
|
482
|
+
commands: {
|
|
483
|
+
list: { method: 'GET', path: '/associations', summary: 'GET /associations [scope: community:associations:read]' },
|
|
484
|
+
get: { method: 'GET', path: '/associations/:association', summary: 'GET /associations/{association} [scope: community:associations:read]' },
|
|
485
|
+
posts: { method: 'GET', path: '/associations/:association/posts', summary: 'GET /associations/{association}/posts [scope: community:associations:read]' },
|
|
486
|
+
},
|
|
487
|
+
},
|
|
488
|
+
policies: {
|
|
489
|
+
summary: 'policies (v1)',
|
|
490
|
+
commands: {
|
|
491
|
+
list: { method: 'GET', path: '/policies', summary: 'GET /policies [scope: business:policies:read]' },
|
|
492
|
+
},
|
|
493
|
+
},
|
|
494
|
+
meetups: {
|
|
495
|
+
summary: 'meetups (v1)',
|
|
496
|
+
commands: {
|
|
497
|
+
list: { method: 'GET', path: '/meetups', summary: 'GET /meetups [scope: community:meetups:read]' },
|
|
498
|
+
get: { method: 'GET', path: '/meetups/:meetup', summary: 'GET /meetups/{meetup} [scope: community:meetups:read]' },
|
|
499
|
+
},
|
|
500
|
+
},
|
|
501
|
+
clones: {
|
|
502
|
+
summary: 'clones (v1)',
|
|
503
|
+
commands: {
|
|
504
|
+
list: { method: 'GET', path: '/clones', summary: 'GET /clones [scope: profile:clones:read]' },
|
|
505
|
+
get: { method: 'GET', path: '/clones/:clone', summary: 'GET /clones/{clone} [scope: profile:clones:read]' },
|
|
506
|
+
create: { method: 'POST', path: '/clones', summary: 'POST /clones [scope: profile:clones:write]' },
|
|
507
|
+
samples: { method: 'POST', path: '/clones/:clone/samples', summary: 'POST /clones/{clone}/samples [scope: profile:clones:write]' },
|
|
508
|
+
iterate: { method: 'POST', path: '/clones/:clone/iterate', summary: 'POST /clones/{clone}/iterate [scope: profile:clones:write]' },
|
|
509
|
+
tts: { method: 'POST', path: '/clones/:clone/tts', summary: 'POST /clones/{clone}/tts [scope: profile:clones:tts]' },
|
|
510
|
+
},
|
|
511
|
+
},
|
|
512
|
+
'vms-communities': {
|
|
513
|
+
summary: 'vms communities (v1)',
|
|
514
|
+
commands: {
|
|
515
|
+
passes: { method: 'GET', path: '/vms/communities/:communitySlug/passes', summary: 'GET /vms/communities/{communitySlug}/passes [scope: vms:passes:read]' },
|
|
516
|
+
'passes-2': { method: 'GET', path: '/vms/communities/:communitySlug/passes/:pass', summary: 'GET /vms/communities/{communitySlug}/passes/{pass} [scope: vms:passes:read]' },
|
|
517
|
+
'create-passes': { method: 'POST', path: '/vms/communities/:communitySlug/passes', summary: 'POST /vms/communities/{communitySlug}/passes [scope: vms:passes:write]' },
|
|
518
|
+
'delete-passes': { method: 'DELETE', path: '/vms/communities/:communitySlug/passes/:pass', summary: 'DELETE /vms/communities/{communitySlug}/passes/{pass} [scope: vms:passes:write]' },
|
|
519
|
+
'gate-events': { method: 'GET', path: '/vms/communities/:communitySlug/gate-events', summary: 'GET /vms/communities/{communitySlug}/gate-events [scope: vms:events:read]' },
|
|
520
|
+
'create-gate-events': { method: 'POST', path: '/vms/communities/:communitySlug/gate-events', summary: 'POST /vms/communities/{communitySlug}/gate-events [scope: vms:events:write]' },
|
|
521
|
+
residents: { method: 'GET', path: '/vms/communities/:communitySlug/residents', summary: 'GET /vms/communities/{communitySlug}/residents [scope: vms:residents:read]' },
|
|
522
|
+
'create-broadcasts': { method: 'POST', path: '/vms/communities/:communitySlug/broadcasts', summary: 'POST /vms/communities/{communitySlug}/broadcasts [scope: vms:broadcasts:write]' },
|
|
523
|
+
gates: { method: 'GET', path: '/vms/communities/:communitySlug/gates', summary: 'GET /vms/communities/{communitySlug}/gates [scope: vms:gates:read]' },
|
|
524
|
+
'gates-2': { method: 'GET', path: '/vms/communities/:communitySlug/gates/:gate', summary: 'GET /vms/communities/{communitySlug}/gates/{gate} [scope: vms:gates:read]' },
|
|
525
|
+
relays: { method: 'GET', path: '/vms/communities/:communitySlug/relays', summary: 'GET /vms/communities/{communitySlug}/relays [scope: vms:relays:read]' },
|
|
526
|
+
'relays-2': { method: 'GET', path: '/vms/communities/:communitySlug/relays/:relay', summary: 'GET /vms/communities/{communitySlug}/relays/{relay} [scope: vms:relays:read]' },
|
|
527
|
+
fire: { method: 'POST', path: '/vms/communities/:communitySlug/relays/:relay/fire', summary: 'POST /vms/communities/{communitySlug}/relays/{relay}/fire [scope: vms:relays:fire]' },
|
|
528
|
+
'parking-permits': { method: 'GET', path: '/vms/communities/:communitySlug/parking/permits', summary: 'GET /vms/communities/{communitySlug}/parking/permits [scope: vms:permits:read]' },
|
|
529
|
+
'parking-allocations': { method: 'GET', path: '/vms/communities/:communitySlug/parking/allocations', summary: 'GET /vms/communities/{communitySlug}/parking/allocations [scope: vms:permits:read]' },
|
|
530
|
+
'create-trafficlogix-event': { method: 'POST', path: '/vms/communities/:communitySlug/trafficlogix/event', summary: 'POST /vms/communities/{communitySlug}/trafficlogix/event [scope: vms:events:write]' },
|
|
531
|
+
'create-telephone-entry-lookup': { method: 'POST', path: '/vms/communities/:communitySlug/telephone-entry/lookup', summary: 'POST /vms/communities/{communitySlug}/telephone-entry/lookup [scope: vms:events:write]' },
|
|
532
|
+
'create-telephone-entry-pin': { method: 'POST', path: '/vms/communities/:communitySlug/telephone-entry/pin', summary: 'POST /vms/communities/{communitySlug}/telephone-entry/pin [scope: vms:events:write]' },
|
|
533
|
+
'create-camera-ai-event': { method: 'POST', path: '/vms/communities/:communitySlug/camera-ai/:vendor/event', summary: 'POST /vms/communities/{communitySlug}/camera-ai/{vendor}/event [scope: vms:events:write]' },
|
|
534
|
+
'predictive-pulse': { method: 'POST', path: '/vms/communities/:communitySlug/gates/:gate/predictive-pulse', summary: 'POST /vms/communities/{communitySlug}/gates/{gate}/predictive-pulse [scope: vms:gates:predictive_pulse]' },
|
|
535
|
+
'create-backup-heartbeat': { method: 'POST', path: '/vms/communities/:communitySlug/backup/heartbeat', summary: 'POST /vms/communities/{communitySlug}/backup/heartbeat [scope: vms:failsafe:sync]' },
|
|
536
|
+
'create-backup-mode': { method: 'POST', path: '/vms/communities/:communitySlug/backup/mode', summary: 'POST /vms/communities/{communitySlug}/backup/mode [scope: vms:failsafe:sync]' },
|
|
537
|
+
'backup-snapshot': { method: 'GET', path: '/vms/communities/:communitySlug/backup/snapshot', summary: 'GET /vms/communities/{communitySlug}/backup/snapshot [scope: vms:failsafe:sync]' },
|
|
538
|
+
'create-backup-relay-fire': { method: 'POST', path: '/vms/communities/:communitySlug/backup/relay-fire', summary: 'POST /vms/communities/{communitySlug}/backup/relay-fire [scope: vms:failsafe:sync]' },
|
|
539
|
+
'create-backup-visitor-verification': { method: 'POST', path: '/vms/communities/:communitySlug/backup/visitor-verification', summary: 'POST /vms/communities/{communitySlug}/backup/visitor-verification [scope: vms:failsafe:sync]' },
|
|
540
|
+
'residents-2': { method: 'GET', path: '/vms/communities/:communitySlug/residents/:resident', summary: 'GET /vms/communities/{communitySlug}/residents/{resident} [scope: vms:residents:read]' },
|
|
541
|
+
},
|
|
542
|
+
},
|
|
543
|
+
goals: {
|
|
544
|
+
summary: 'goals (v1)',
|
|
545
|
+
commands: {
|
|
546
|
+
list: { method: 'GET', path: '/goals', summary: 'GET /goals [scope: goals:read]' },
|
|
547
|
+
},
|
|
548
|
+
},
|
|
549
|
+
epics: {
|
|
550
|
+
summary: 'epics (v1)',
|
|
551
|
+
commands: {
|
|
552
|
+
list: { method: 'GET', path: '/epics', summary: 'GET /epics [scope: goals:read]' },
|
|
553
|
+
},
|
|
554
|
+
},
|
|
555
|
+
'professional-profile': {
|
|
556
|
+
summary: 'professional profile (v1)',
|
|
557
|
+
commands: {
|
|
558
|
+
list: { method: 'GET', path: '/professional-profile', summary: 'GET /professional-profile [scope: professional_profile:read]' },
|
|
559
|
+
portfolio: { method: 'GET', path: '/professional-profile/portfolio', summary: 'GET /professional-profile/portfolio [scope: professional_profile:read]' },
|
|
560
|
+
},
|
|
561
|
+
},
|
|
562
|
+
'professional-profiles': {
|
|
563
|
+
summary: 'professional profiles (v1)',
|
|
564
|
+
commands: {
|
|
565
|
+
get: { method: 'GET', path: '/professional-profiles/:handle', summary: 'GET /professional-profiles/{handle} [scope: professional_profile:read]' },
|
|
566
|
+
},
|
|
567
|
+
},
|
|
568
|
+
accounting: {
|
|
569
|
+
summary: 'accounting (v1)',
|
|
570
|
+
commands: {
|
|
571
|
+
'chart-of-accounts': { method: 'GET', path: '/accounting/chart-of-accounts', summary: 'GET /accounting/chart-of-accounts [scope: accounting:read]' },
|
|
572
|
+
'journal-entries': { method: 'GET', path: '/accounting/journal-entries', summary: 'GET /accounting/journal-entries [scope: accounting:read]' },
|
|
573
|
+
'trial-balance': { method: 'GET', path: '/accounting/trial-balance', summary: 'GET /accounting/trial-balance [scope: accounting:read]' },
|
|
574
|
+
'statements-income': { method: 'GET', path: '/accounting/statements/income', summary: 'GET /accounting/statements/income [scope: accounting:read]' },
|
|
575
|
+
'statements-balance-sheet': { method: 'GET', path: '/accounting/statements/balance-sheet', summary: 'GET /accounting/statements/balance-sheet [scope: accounting:read]' },
|
|
576
|
+
'consolidated-trial-balance': { method: 'GET', path: '/accounting/consolidated/trial-balance', summary: 'GET /accounting/consolidated/trial-balance [scope: accounting:read]' },
|
|
577
|
+
'consolidated-income': { method: 'GET', path: '/accounting/consolidated/income', summary: 'GET /accounting/consolidated/income [scope: accounting:read]' },
|
|
578
|
+
'consolidated-balance-sheet': { method: 'GET', path: '/accounting/consolidated/balance-sheet', summary: 'GET /accounting/consolidated/balance-sheet [scope: accounting:read]' },
|
|
579
|
+
'tax-liability': { method: 'GET', path: '/accounting/tax-liability', summary: 'GET /accounting/tax-liability [scope: accounting:read]' },
|
|
580
|
+
'segregation-of-duties': { method: 'GET', path: '/accounting/segregation-of-duties', summary: 'GET /accounting/segregation-of-duties [scope: accounting:read]' },
|
|
581
|
+
'audit-trail': { method: 'GET', path: '/accounting/audit-trail', summary: 'GET /accounting/audit-trail [scope: accounting:read]' },
|
|
582
|
+
'e-invoices': { method: 'GET', path: '/accounting/e-invoices', summary: 'GET /accounting/e-invoices [scope: accounting:read]' },
|
|
583
|
+
'bank-reconciliations': { method: 'GET', path: '/accounting/bank-reconciliations', summary: 'GET /accounting/bank-reconciliations [scope: accounting:read]' },
|
|
584
|
+
},
|
|
585
|
+
},
|
|
586
|
+
receivables: {
|
|
587
|
+
summary: 'receivables (v1)',
|
|
588
|
+
commands: {
|
|
589
|
+
dunning: { method: 'GET', path: '/receivables/dunning', summary: 'GET /receivables/dunning [scope: receivables:read]' },
|
|
590
|
+
subscriptions: { method: 'GET', path: '/receivables/subscriptions', summary: 'GET /receivables/subscriptions [scope: receivables:read]' },
|
|
591
|
+
},
|
|
592
|
+
},
|
|
593
|
+
payables: {
|
|
594
|
+
summary: 'payables (v1)',
|
|
595
|
+
commands: {
|
|
596
|
+
'discount-opportunities': { method: 'GET', path: '/payables/discount-opportunities', summary: 'GET /payables/discount-opportunities [scope: payables:read]' },
|
|
597
|
+
},
|
|
598
|
+
},
|
|
599
|
+
procurement: {
|
|
600
|
+
summary: 'procurement (v1)',
|
|
601
|
+
commands: {
|
|
602
|
+
suppliers: { method: 'GET', path: '/procurement/suppliers', summary: 'GET /procurement/suppliers [scope: procurement:read]' },
|
|
603
|
+
'purchase-orders': { method: 'GET', path: '/procurement/purchase-orders', summary: 'GET /procurement/purchase-orders [scope: procurement:read]' },
|
|
604
|
+
requisitions: { method: 'GET', path: '/procurement/requisitions', summary: 'GET /procurement/requisitions [scope: procurement:read]' },
|
|
605
|
+
},
|
|
606
|
+
},
|
|
607
|
+
inventory: {
|
|
608
|
+
summary: 'inventory (v1)',
|
|
609
|
+
commands: {
|
|
610
|
+
'stock-levels': { method: 'GET', path: '/inventory/stock-levels', summary: 'GET /inventory/stock-levels [scope: inventory:read]' },
|
|
611
|
+
valuation: { method: 'GET', path: '/inventory/valuation', summary: 'GET /inventory/valuation [scope: inventory:read]' },
|
|
612
|
+
warehouses: { method: 'GET', path: '/inventory/warehouses', summary: 'GET /inventory/warehouses [scope: inventory:read]' },
|
|
613
|
+
},
|
|
614
|
+
},
|
|
615
|
+
hr: {
|
|
616
|
+
summary: 'hr (v1)',
|
|
617
|
+
commands: {
|
|
618
|
+
employees: { method: 'GET', path: '/hr/employees', summary: 'GET /hr/employees [scope: hr:read]' },
|
|
619
|
+
headcount: { method: 'GET', path: '/hr/headcount', summary: 'GET /hr/headcount [scope: hr:read]' },
|
|
620
|
+
},
|
|
621
|
+
},
|
|
622
|
+
'fixed-assets': {
|
|
623
|
+
summary: 'fixed assets (v1)',
|
|
624
|
+
commands: {
|
|
625
|
+
list: { method: 'GET', path: '/fixed-assets', summary: 'GET /fixed-assets [scope: fixed_assets:read]' },
|
|
626
|
+
overview: { method: 'GET', path: '/fixed-assets/overview', summary: 'GET /fixed-assets/overview [scope: fixed_assets:read]' },
|
|
627
|
+
},
|
|
628
|
+
},
|
|
629
|
+
manufacturing: {
|
|
630
|
+
summary: 'manufacturing (v1)',
|
|
631
|
+
commands: {
|
|
632
|
+
'work-orders': { method: 'GET', path: '/manufacturing/work-orders', summary: 'GET /manufacturing/work-orders [scope: manufacturing:read]' },
|
|
633
|
+
'bills-of-materials': { method: 'GET', path: '/manufacturing/bills-of-materials', summary: 'GET /manufacturing/bills-of-materials [scope: manufacturing:read]' },
|
|
634
|
+
},
|
|
635
|
+
},
|
|
636
|
+
'tech-tree': {
|
|
637
|
+
summary: 'tech tree (v1)',
|
|
638
|
+
commands: {
|
|
639
|
+
nodes: { method: 'GET', path: '/tech-tree/nodes', summary: 'GET /tech-tree/nodes [scope: tech_tree:read]' },
|
|
640
|
+
graph: { method: 'GET', path: '/tech-tree/graph', summary: 'GET /tech-tree/graph [scope: tech_tree:read]' },
|
|
641
|
+
},
|
|
642
|
+
},
|
|
643
|
+
influence: {
|
|
644
|
+
summary: 'influence (v1)',
|
|
645
|
+
commands: {
|
|
646
|
+
layers: { method: 'GET', path: '/influence/layers', summary: 'GET /influence/layers [scope: business:influence:read]' },
|
|
647
|
+
'missions-context': { method: 'GET', path: '/influence/missions/:mission/context', summary: 'GET /influence/missions/{mission}/context [scope: business:influence:read]' },
|
|
648
|
+
},
|
|
649
|
+
},
|
|
650
|
+
qms: {
|
|
651
|
+
summary: 'qms (v1)',
|
|
652
|
+
commands: {
|
|
653
|
+
readiness: { method: 'GET', path: '/qms/readiness', summary: 'GET /qms/readiness [scope: business:qms:read]' },
|
|
654
|
+
objectives: { method: 'GET', path: '/qms/objectives', summary: 'GET /qms/objectives [scope: business:qms:read]' },
|
|
655
|
+
risks: { method: 'GET', path: '/qms/risks', summary: 'GET /qms/risks [scope: business:qms:read]' },
|
|
656
|
+
capas: { method: 'GET', path: '/qms/capas', summary: 'GET /qms/capas [scope: business:qms:read]' },
|
|
657
|
+
audits: { method: 'GET', path: '/qms/audits', summary: 'GET /qms/audits [scope: business:qms:read]' },
|
|
658
|
+
competence: { method: 'GET', path: '/qms/competence', summary: 'GET /qms/competence [scope: business:qms:read]' },
|
|
659
|
+
documents: { method: 'GET', path: '/qms/documents', summary: 'GET /qms/documents [scope: business:qms:read]' },
|
|
660
|
+
certification: { method: 'GET', path: '/qms/certification', summary: 'GET /qms/certification [scope: business:qms:read]' },
|
|
661
|
+
obligations: { method: 'GET', path: '/qms/obligations', summary: 'GET /qms/obligations [scope: business:qms:read]' },
|
|
662
|
+
},
|
|
663
|
+
},
|
|
664
|
+
'data-objects': {
|
|
665
|
+
summary: 'data objects (v1)',
|
|
666
|
+
commands: {
|
|
667
|
+
list: { method: 'GET', path: '/data/objects', summary: 'GET /data/objects [scope: data:objects:read]' },
|
|
668
|
+
schema: { method: 'GET', path: '/data/objects/:object/schema', summary: 'GET /data/objects/{object}/schema [scope: data:objects:read]' },
|
|
669
|
+
create: { method: 'POST', path: '/data/objects', summary: 'POST /data/objects [scope: data:objects:write]' },
|
|
670
|
+
records: { method: 'GET', path: '/data/objects/:object/records', summary: 'GET /data/objects/{object}/records [scope: data:records:read]' },
|
|
671
|
+
'create-records': { method: 'POST', path: '/data/objects/:object/records', summary: 'POST /data/objects/{object}/records [scope: data:records:write]' },
|
|
672
|
+
upsert: { method: 'POST', path: '/data/objects/:object/records/upsert', summary: 'POST /data/objects/{object}/records/upsert [scope: data:records:write]' },
|
|
673
|
+
},
|
|
674
|
+
},
|
|
675
|
+
'data-records': {
|
|
676
|
+
summary: 'data records (v1)',
|
|
677
|
+
commands: {
|
|
678
|
+
get: { method: 'GET', path: '/data/records/:uuid', summary: 'GET /data/records/{uuid} [scope: data:records:read]' },
|
|
679
|
+
update: { method: 'PATCH', path: '/data/records/:uuid', summary: 'PATCH /data/records/{uuid} [scope: data:records:write]' },
|
|
680
|
+
delete: { method: 'DELETE', path: '/data/records/:uuid', summary: 'DELETE /data/records/{uuid} [scope: data:records:write]' },
|
|
681
|
+
},
|
|
682
|
+
},
|
|
683
|
+
kits: {
|
|
684
|
+
summary: 'kits (v1)',
|
|
685
|
+
commands: {
|
|
686
|
+
list: { method: 'GET', path: '/kits', summary: 'GET /kits [scope: kits:read]' },
|
|
687
|
+
},
|
|
688
|
+
},
|
|
689
|
+
'business-dna': {
|
|
690
|
+
summary: 'business dna (v1)',
|
|
691
|
+
commands: {
|
|
692
|
+
list: { method: 'GET', path: '/business/dna', summary: 'GET /business/dna [scope: business:dna:read]' },
|
|
693
|
+
},
|
|
694
|
+
},
|
|
695
|
+
'business-plan': {
|
|
696
|
+
summary: 'business plan (v1)',
|
|
697
|
+
commands: {
|
|
698
|
+
list: { method: 'GET', path: '/business/plan', summary: 'GET /business/plan [scope: business:plan:read]' },
|
|
699
|
+
},
|
|
700
|
+
},
|
|
701
|
+
iot: {
|
|
702
|
+
summary: 'iot (v1)',
|
|
703
|
+
commands: {
|
|
704
|
+
devices: { method: 'GET', path: '/iot/devices', summary: 'GET /iot/devices [scope: iot:read]' },
|
|
705
|
+
'create-devices': { method: 'POST', path: '/iot/devices', summary: 'POST /iot/devices [scope: iot:write]' },
|
|
706
|
+
'create-readings': { method: 'POST', path: '/iot/readings', summary: 'POST /iot/readings [scope: iot:write]' },
|
|
707
|
+
'create-alarms': { method: 'POST', path: '/iot/alarms', summary: 'POST /iot/alarms [scope: iot:write]' },
|
|
708
|
+
},
|
|
709
|
+
},
|
|
710
|
+
property: {
|
|
711
|
+
summary: 'property (v1)',
|
|
712
|
+
commands: {
|
|
713
|
+
locations: { method: 'GET', path: '/property/locations', summary: 'GET /property/locations [scope: property:read]' },
|
|
714
|
+
'work-orders': { method: 'GET', path: '/property/work-orders', summary: 'GET /property/work-orders [scope: property:read]' },
|
|
715
|
+
assets: { method: 'GET', path: '/property/assets', summary: 'GET /property/assets [scope: property:read]' },
|
|
716
|
+
},
|
|
717
|
+
},
|
|
718
|
+
funnels: {
|
|
719
|
+
summary: 'funnels (v1)',
|
|
720
|
+
commands: {
|
|
721
|
+
list: { method: 'GET', path: '/funnels', summary: 'GET /funnels [scope: funnels:read]' },
|
|
722
|
+
},
|
|
723
|
+
},
|
|
724
|
+
'ad-simulations': {
|
|
725
|
+
summary: 'ad simulations (v1)',
|
|
726
|
+
commands: {
|
|
727
|
+
list: { method: 'GET', path: '/ad-simulations', summary: 'GET /ad-simulations [scope: business:ad-simulations:read]' },
|
|
728
|
+
},
|
|
729
|
+
},
|
|
730
|
+
sites: {
|
|
731
|
+
summary: 'sites (v1)',
|
|
732
|
+
commands: {
|
|
733
|
+
'store-locations': { method: 'GET', path: '/sites/:token/store-locations', summary: 'GET /sites/{token}/store-locations' },
|
|
734
|
+
products: { method: 'GET', path: '/sites/:token/products', summary: 'GET /sites/{token}/products' },
|
|
735
|
+
search: { method: 'GET', path: '/sites/:token/products/search', summary: 'GET /sites/{token}/products/search' },
|
|
736
|
+
'products-2': { method: 'GET', path: '/sites/:token/products/:slug', summary: 'GET /sites/{token}/products/{slug}' },
|
|
737
|
+
related: { method: 'GET', path: '/sites/:token/products/:slug/related', summary: 'GET /sites/{token}/products/{slug}/related' },
|
|
738
|
+
reviews: { method: 'GET', path: '/sites/:token/products/:slug/reviews', summary: 'GET /sites/{token}/products/{slug}/reviews' },
|
|
739
|
+
collections: { method: 'GET', path: '/sites/:token/collections', summary: 'GET /sites/{token}/collections' },
|
|
740
|
+
'collections-products': { method: 'GET', path: '/sites/:token/collections/:slug/products', summary: 'GET /sites/{token}/collections/{slug}/products' },
|
|
741
|
+
cart: { method: 'GET', path: '/sites/:token/cart', summary: 'GET /sites/{token}/cart' },
|
|
742
|
+
'cart-count': { method: 'GET', path: '/sites/:token/cart/count', summary: 'GET /sites/{token}/cart/count' },
|
|
743
|
+
'create-cart-items': { method: 'POST', path: '/sites/:token/cart/items', summary: 'POST /sites/{token}/cart/items' },
|
|
744
|
+
'update-cart-items': { method: 'PUT', path: '/sites/:token/cart/items/:itemId', summary: 'PUT /sites/{token}/cart/items/{itemId}' },
|
|
745
|
+
'delete-cart-items': { method: 'DELETE', path: '/sites/:token/cart/items/:itemId', summary: 'DELETE /sites/{token}/cart/items/{itemId}' },
|
|
746
|
+
'delete-cart': { method: 'DELETE', path: '/sites/:token/cart', summary: 'DELETE /sites/{token}/cart' },
|
|
747
|
+
'create-cart-apply-code': { method: 'POST', path: '/sites/:token/cart/apply-code', summary: 'POST /sites/{token}/cart/apply-code' },
|
|
748
|
+
'delete-cart-apply-code': { method: 'DELETE', path: '/sites/:token/cart/apply-code/:applicationId', summary: 'DELETE /sites/{token}/cart/apply-code/{applicationId}' },
|
|
749
|
+
'create-checkout-payment-intent': { method: 'POST', path: '/sites/:token/checkout/payment-intent', summary: 'POST /sites/{token}/checkout/payment-intent' },
|
|
750
|
+
'create-checkout-process': { method: 'POST', path: '/sites/:token/checkout/process', summary: 'POST /sites/{token}/checkout/process' },
|
|
751
|
+
orders: { method: 'GET', path: '/sites/:token/orders/:number', summary: 'GET /sites/{token}/orders/{number}' },
|
|
752
|
+
'blog-articles': { method: 'GET', path: '/sites/:token/blog-articles', summary: 'GET /sites/{token}/blog-articles' },
|
|
753
|
+
'create-contact-submissions': { method: 'POST', path: '/sites/:token/contact-submissions', summary: 'POST /sites/{token}/contact-submissions' },
|
|
754
|
+
},
|
|
755
|
+
},
|
|
756
|
+
'travel-flights': {
|
|
757
|
+
summary: 'travel flights (v1)',
|
|
758
|
+
commands: {
|
|
759
|
+
get: { method: 'GET', path: '/travel/flights/:flight', summary: 'GET /travel/flights/{flight} [scope: travel:flights:read]' },
|
|
760
|
+
},
|
|
761
|
+
},
|
|
762
|
+
'travel-stays': {
|
|
763
|
+
summary: 'travel stays (v1)',
|
|
764
|
+
commands: {
|
|
765
|
+
get: { method: 'GET', path: '/travel/stays/:stay', summary: 'GET /travel/stays/{stay} [scope: travel:stays:read]' },
|
|
766
|
+
},
|
|
767
|
+
},
|
|
768
|
+
'travel-cars': {
|
|
769
|
+
summary: 'travel cars (v1)',
|
|
770
|
+
commands: {
|
|
771
|
+
get: { method: 'GET', path: '/travel/cars/:car', summary: 'GET /travel/cars/{car} [scope: travel:cars:read]' },
|
|
772
|
+
},
|
|
773
|
+
},
|
|
181
774
|
};
|