@myapihq/cli 2.20.1 → 2.21.1

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.
@@ -2,6 +2,11 @@ import { audience as sdkAudience } from '@myapihq/sdk';
2
2
  import { requireConfig } from '../config.js';
3
3
  import { success, error, printTable, info, printJson } from '../output.js';
4
4
  import { requireOrg, requireArg } from '../helpers.js';
5
+ import { retryFunds } from '../utils.js';
6
+ // Billable: the backend reserves funds before serving these, so an empty
7
+ // wallet with an auto-recharge in flight answers 402 in_flight with a
8
+ // retry hint. retryFunds waits it out instead of killing an unattended
9
+ // run. Only create/update/members/refresh gate — list, get and delete are free and stay unwrapped.
5
10
  export const EXPOSES = [
6
11
  'POST /audience/orgs/{org_id}/audiences',
7
12
  'GET /audience/orgs/{org_id}/audiences',
@@ -59,9 +64,9 @@ async function create(nameArg, flags) {
59
64
  }
60
65
  const filter = parseFilter(flags.filter);
61
66
  const description = typeof flags.description === 'string' ? flags.description : undefined;
62
- const res = await sdkAudience.createAudience(config.api_key, orgId, {
67
+ const res = await retryFunds(() => sdkAudience.createAudience(config.api_key, orgId, {
63
68
  name, source: source, filter, description,
64
- });
69
+ }));
65
70
  if (flags.json) {
66
71
  printJson(res);
67
72
  return;
@@ -102,7 +107,7 @@ async function update(id, flags) {
102
107
  if (Object.keys(patch).length === 0) {
103
108
  error('Pass at least one of --name / --description / --filter to update.');
104
109
  }
105
- const res = await sdkAudience.updateAudience(config.api_key, orgId, id, patch);
110
+ const res = await retryFunds(() => sdkAudience.updateAudience(config.api_key, orgId, id, patch));
106
111
  if (flags.json) {
107
112
  printJson(res);
108
113
  return;
@@ -125,7 +130,7 @@ async function members(id, flags) {
125
130
  opts.limit = flags.limit;
126
131
  if (typeof flags.offset === 'number')
127
132
  opts.offset = flags.offset;
128
- const res = await sdkAudience.getAudienceMembers(config.api_key, orgId, id, opts);
133
+ const res = await retryFunds(() => sdkAudience.getAudienceMembers(config.api_key, orgId, id, opts));
129
134
  if (flags.json) {
130
135
  printJson(res);
131
136
  return;
@@ -161,7 +166,7 @@ async function refresh(id, flags) {
161
166
  const config = requireConfig();
162
167
  const orgId = requireOrg(flags, config, 'myapi audience refresh <id> [--org <id>]');
163
168
  requireArg(id, 'id', 'myapi audience refresh <id> [--org <id>]');
164
- const res = await sdkAudience.refreshAudience(config.api_key, orgId, id);
169
+ const res = await retryFunds(() => sdkAudience.refreshAudience(config.api_key, orgId, id));
165
170
  if (flags.json) {
166
171
  printJson(res);
167
172
  return;
@@ -2,6 +2,11 @@ import { company as sdkCompany } from '@myapihq/sdk';
2
2
  import { requireConfig } from '../config.js';
3
3
  import { error, printTable, info, printJson } from '../output.js';
4
4
  import { requireOrg, requireArg } from '../helpers.js';
5
+ import { retryFunds } from '../utils.js';
6
+ // Billable: the backend reserves funds before serving these, so an empty
7
+ // wallet with an auto-recharge in flight answers 402 in_flight with a
8
+ // retry hint. retryFunds waits it out instead of killing an unattended
9
+ // run. Both handlers gate; there is no free company call.
5
10
  export const EXPOSES = [
6
11
  'POST /company/orgs/{org_id}/search',
7
12
  'GET /company/orgs/{org_id}/{company_id}',
@@ -72,7 +77,7 @@ async function search(flags) {
72
77
  const config = requireConfig();
73
78
  const orgId = requireOrg(flags, config, 'myapi company search [filters...] [--org <id>]');
74
79
  const options = buildSearch(flags);
75
- const res = await sdkCompany.searchCompanies(config.api_key, orgId, options);
80
+ const res = await retryFunds(() => sdkCompany.searchCompanies(config.api_key, orgId, options));
76
81
  if (flags.json) {
77
82
  printJson(res);
78
83
  return;
@@ -94,7 +99,7 @@ async function get(companyId, flags) {
94
99
  const orgId = requireOrg(flags, config, 'myapi company get <company_id> [--include-people N] [--org <id>]');
95
100
  requireArg(companyId, 'company_id', 'myapi company get <company_id> [--include-people N] [--org <id>]');
96
101
  const includePeople = typeof flags['include-people'] === 'number' ? flags['include-people'] : undefined;
97
- const res = await sdkCompany.getCompany(config.api_key, orgId, companyId, includePeople);
102
+ const res = await retryFunds(() => sdkCompany.getCompany(config.api_key, orgId, companyId, includePeople));
98
103
  printJson(res);
99
104
  }
100
105
  export const SUBCOMMAND_USAGE = {
@@ -5,4 +5,5 @@ export declare const EXPOSES: Exposes;
5
5
  export declare const SCHEMA: FlagSchema;
6
6
  export declare function _parseJsonObjectFlag(raw: unknown, flagName: string): Record<string, unknown> | undefined;
7
7
  export declare const SUBCOMMAND_USAGE: Record<string, string>;
8
+ export declare const NESTED_SUBCOMMAND_USAGE: Record<string, Record<string, string>>;
8
9
  export declare function run(subcommand: string | undefined, args: string[], flags: Flags): Promise<void>;
@@ -2,13 +2,16 @@ import * as fs from 'fs';
2
2
  import { llm as sdkLlm } from '@myapihq/sdk';
3
3
  import { requireConfig } from '../config.js';
4
4
  import { error, info, printTable, printJson } from '../output.js';
5
- import { requireOrg } from '../helpers.js';
5
+ import { requireOrg, requireArg } from '../helpers.js';
6
6
  import { retryFunds } from '../utils.js';
7
7
  export const EXPOSES = [
8
8
  'POST /llm/orgs/{org_id}/complete',
9
9
  'POST /llm/orgs/{org_id}/embed',
10
10
  'GET /llm/orgs/{org_id}/models',
11
11
  'POST /llm/orgs/{org_id}/tasks/{verb}',
12
+ 'POST /llm/orgs/{org_id}/caches',
13
+ 'GET /llm/orgs/{org_id}/caches',
14
+ 'DELETE /llm/orgs/{org_id}/caches/{id}',
12
15
  ];
13
16
  export const SCHEMA = {
14
17
  org: 'string',
@@ -30,6 +33,10 @@ export const SCHEMA = {
30
33
  context: 'string',
31
34
  prompt: 'string',
32
35
  tier: 'string',
36
+ // context caches
37
+ cache: 'string',
38
+ ttl: 'number',
39
+ yes: 'boolean',
33
40
  };
34
41
  // Cache the catalog once per CLI invocation so verbs (which don't take a
35
42
  // --model flag) can fall back to "the first chat model" without an extra
@@ -161,6 +168,7 @@ async function complete(promptArg, flags) {
161
168
  const res = await retryFunds(() => sdkLlm.complete(config.api_key, orgId, {
162
169
  model,
163
170
  messages,
171
+ cache_id: typeof flags.cache === 'string' && flags.cache ? flags.cache : undefined,
164
172
  max_tokens: typeof flags['max-tokens'] === 'number' ? flags['max-tokens'] : undefined,
165
173
  temperature: typeof flags.temperature === 'number' ? flags.temperature : undefined,
166
174
  stop,
@@ -171,7 +179,12 @@ async function complete(promptArg, flags) {
171
179
  }
172
180
  info(res.content);
173
181
  // Usage footer on stderr so it doesn't pollute piped output.
174
- process.stderr.write(`\n— ${res.model} · ${res.usage.input_tokens}+${res.usage.output_tokens ?? 0} tokens · ${fmtCostCents(res.usage.cost_cents)} · ${res.finish_reason}\n`);
182
+ // Cached tokens are shown as "N of M cached", never added to the input
183
+ // count: `cached_input_tokens` is a SUBSET of `input_tokens`, and a footer
184
+ // that summed them would report more tokens than were sent.
185
+ const cached = res.usage.cached_input_tokens ?? 0;
186
+ const cachedNote = cached > 0 ? ` (${cached} cached)` : '';
187
+ process.stderr.write(`\n— ${res.model} · ${res.usage.input_tokens}${cachedNote}+${res.usage.output_tokens ?? 0} tokens · ${fmtCostCents(res.usage.cost_cents)} · ${res.finish_reason}\n`);
175
188
  }
176
189
  async function embed(inputArg, flags) {
177
190
  const config = requireConfig();
@@ -209,6 +222,81 @@ async function listModels(flags) {
209
222
  'out ¢/1M': m.output_cost_per_1m_cents != null ? m.output_cost_per_1m_cents : '',
210
223
  })), { flags, empty: 'No models available.' });
211
224
  }
225
+ // ── context caches ────────────────────────────────────────────────────────
226
+ // A cache bills for EXISTING, not for being read: storage accrues per hour
227
+ // from the moment it is created. That is why this surface is explicit and why
228
+ // `delete` is documented as the way to stop paying — the alternative, waiting
229
+ // out the TTL, means paying the TTL in full.
230
+ const CACHE_HELP = `Usage: myapi llm cache <create|list|delete>
231
+
232
+ create Store a prompt prefix (--file <path>, --model <id>, [--ttl <secs>])
233
+ list Live caches with size, expiry, and what they cost to hold
234
+ delete Delete a cache and stop paying for it
235
+
236
+ A cache costs a TENTH to re-read: pass it to complete with --cache <id>.
237
+ It is bound to ONE model — a cache made for one model cannot be read by
238
+ another. Storage is billed per hour whether or not anybody reads it, so
239
+ delete one you are done with rather than waiting out its TTL.`;
240
+ async function cacheCreate(flags) {
241
+ const config = requireConfig();
242
+ const orgId = requireOrg(flags, config, 'myapi llm cache create --file <path> [--model <id>] [--ttl <seconds>] [--org <id>]');
243
+ const content = readPromptArg(undefined, flags);
244
+ if (!content.trim()) {
245
+ error('Empty content. Pass --file <path> (the content is long by definition — that is the reason to cache it).');
246
+ }
247
+ const model = typeof flags.model === 'string' && flags.model
248
+ ? flags.model
249
+ : await defaultChatModel(config.api_key, orgId);
250
+ const ttl = typeof flags.ttl === 'number' ? flags.ttl : undefined;
251
+ const res = await retryFunds(() => sdkLlm.createCache(config.api_key, orgId, {
252
+ model,
253
+ content,
254
+ ttl_seconds: ttl,
255
+ }));
256
+ if (flags.json) {
257
+ printJson(res);
258
+ return;
259
+ }
260
+ info(`Cached ${res.tokens} tokens for ${res.model} → ${res.id}`);
261
+ info(`Expires ${res.expires_at}. Read it with: myapi llm complete --model ${res.model} --cache ${res.id} "..."`);
262
+ // Said on create because this is the moment the meter starts, and the
263
+ // request's own --ttl is not what was necessarily granted.
264
+ info(`Billed per hour until then whether or not you read it — "myapi llm cache delete ${res.id}" stops the charge.`);
265
+ }
266
+ async function cacheList(flags) {
267
+ const config = requireConfig();
268
+ const orgId = requireOrg(flags, config, 'myapi llm cache list [--org <id>]');
269
+ const res = await sdkLlm.listCaches(config.api_key, orgId);
270
+ if (flags.json) {
271
+ printJson(res);
272
+ return;
273
+ }
274
+ printTable(res.caches.map(c => ({
275
+ id: c.id,
276
+ model: c.model,
277
+ tokens: c.tokens,
278
+ expires_at: c.expires_at,
279
+ })), { flags, empty: 'No live caches. (Expired ones are not listed — and are no longer billed.)' });
280
+ }
281
+ async function cacheDelete(args, flags) {
282
+ const config = requireConfig();
283
+ const orgId = requireOrg(flags, config, 'myapi llm cache delete <id> [--org <id>]');
284
+ const id = requireArg(args[0], '<id>', 'myapi llm cache delete <id>');
285
+ await sdkLlm.deleteCache(config.api_key, orgId, id);
286
+ info(`Deleted ${id}. Storage for it is no longer billed.`);
287
+ }
288
+ async function cacheRun(sub, args, flags) {
289
+ if (!sub || flags.help) {
290
+ info(CACHE_HELP);
291
+ return;
292
+ }
293
+ switch (sub) {
294
+ case 'create': return cacheCreate(flags);
295
+ case 'list': return cacheList(flags);
296
+ case 'delete': return cacheDelete(args, flags);
297
+ default: error(`Unknown subcommand: llm cache ${sub}. Run "myapi llm cache --help" for the list.`);
298
+ }
299
+ }
212
300
  // ── verbs ────────────────────────────────────────────────────────────────
213
301
  function printVerbFooter(usage) {
214
302
  process.stderr.write(`\n— tier=${usage.tier_used} · ${usage.tokens_in}+${usage.tokens_out} tokens · ${fmtCostCents(usage.cost_cents)}\n`);
@@ -315,7 +403,7 @@ async function draft(inputArg, flags) {
315
403
  }
316
404
  // ── help ─────────────────────────────────────────────────────────────────
317
405
  export const SUBCOMMAND_USAGE = {
318
- complete: `myapi llm complete "<prompt>" [--model <id>]
406
+ complete: `myapi llm complete "<prompt>" [--model <id>] [--cache <id>]
319
407
  [--system "<system msg>"] [--max-tokens N] [--temperature 0..1]
320
408
  [--stop <csv>] [--file <path>] [--org <id>] [--json]
321
409
 
@@ -326,9 +414,31 @@ export const SUBCOMMAND_USAGE = {
326
414
  one-line usage footer (tokens + cost in cents) goes to stderr so it
327
415
  doesn't pollute piped output.
328
416
 
417
+ --cache <id> re-reads a stored prefix at a TENTH the input price; make one
418
+ with "myapi llm cache create". The cache must belong to the same model.
419
+
329
420
  Examples:
330
421
  myapi llm complete "summarize: $(cat README.md)"
331
- cat draft.md | myapi llm complete - --system "You are an editor" --max-tokens 200`,
422
+ cat draft.md | myapi llm complete - --system "You are an editor" --max-tokens 200
423
+ myapi llm complete "what changed?" --model myapi-fast --cache cch_...`,
424
+ cache: `myapi llm cache <create|list|delete>
425
+
426
+ create myapi llm cache create --file <path> [--model <id>] [--ttl <secs>]
427
+ list myapi llm cache list
428
+ delete myapi llm cache delete <id>
429
+
430
+ Store a prompt prefix once and re-read it at a TENTH the input price. Worth
431
+ it for anything long you send on every call — a system prompt, a reference
432
+ document, a schema.
433
+
434
+ Two things that cost money if you skip them:
435
+ - A cache bills for EXISTING, not for being read. Storage accrues per hour
436
+ from creation whether or not anybody reads it. "cache delete" is how you
437
+ stop paying; waiting out the TTL means paying the whole TTL.
438
+ - --ttl is capped at 24h. A longer request is silently shortened, not
439
+ refused, so read expires_at from the reply rather than trusting your input.
440
+
441
+ A cache belongs to ONE model and cannot be read by another.`,
332
442
  embed: `myapi llm embed "<text>" [--model <id>] [--file <path>] [--org <id>] [--json]
333
443
 
334
444
  Embed text into a dense vector. --model is optional when the catalog serves
@@ -381,6 +491,13 @@ export const SUBCOMMAND_USAGE = {
381
491
  --context '{"recipient":"a new signup","product":"MyAPI"}'
382
492
  cat inbound.eml | myapi llm draft - --kind reply --prompt "Acknowledge and ask for the order ID"`,
383
493
  };
494
+ export const NESTED_SUBCOMMAND_USAGE = {
495
+ cache: {
496
+ 'create': 'myapi llm cache create --file <path> [--model <id>] [--ttl <seconds>] [--org <id>] [--json]',
497
+ 'list': 'myapi llm cache list [--org <id>] [--json]',
498
+ 'delete': 'myapi llm cache delete <id> [--org <id>]',
499
+ },
500
+ };
384
501
  export async function run(subcommand, args, flags) {
385
502
  if (!subcommand || (flags.help && !subcommand)) {
386
503
  info(`Usage: myapi llm <subcommand>
@@ -392,6 +509,7 @@ Two surfaces:
392
509
  is never named in the response.
393
510
 
394
511
  Subcommands:
512
+ cache Store a prompt prefix; re-read it at a tenth the input price
395
513
  classify Pick a label from a set
396
514
  complete Raw chat completion against a catalog model
397
515
  draft Write something (email | reply | message | …)
@@ -421,6 +539,7 @@ for your own reasoning. The agent has its own model already.`);
421
539
  case 'extract': return extract(args[0], flags);
422
540
  case 'summarize': return summarize(args[0], flags);
423
541
  case 'draft': return draft(args[0], flags);
542
+ case 'cache': return cacheRun(args[0], args.slice(1), flags);
424
543
  default: error(`Unknown subcommand: ${subcommand}. Run "myapi llm --help" for valid subcommands.`);
425
544
  }
426
545
  }
@@ -2,6 +2,11 @@ import { people as sdkPeople } from '@myapihq/sdk';
2
2
  import { requireConfig } from '../config.js';
3
3
  import { error, printTable, info, printJson } from '../output.js';
4
4
  import { requireOrg, requireArg } from '../helpers.js';
5
+ import { retryFunds } from '../utils.js';
6
+ // Billable: the backend reserves funds before serving these, so an empty
7
+ // wallet with an auto-recharge in flight answers 402 in_flight with a
8
+ // retry hint. retryFunds waits it out instead of killing an unattended
9
+ // run. Both handlers gate; there is no free people call.
5
10
  export const EXPOSES = [
6
11
  'POST /people/orgs/{org_id}/search',
7
12
  'GET /people/orgs/{org_id}/{person_id}',
@@ -61,7 +66,7 @@ async function search(flags) {
61
66
  const config = requireConfig();
62
67
  const orgId = requireOrg(flags, config, 'myapi people search [filters...] [--org <id>]');
63
68
  const filter = buildFilter(flags);
64
- const res = await sdkPeople.searchPeople(config.api_key, orgId, filter);
69
+ const res = await retryFunds(() => sdkPeople.searchPeople(config.api_key, orgId, filter));
65
70
  if (flags.json) {
66
71
  printJson(res);
67
72
  return;
@@ -80,7 +85,7 @@ async function get(personId, flags) {
80
85
  const config = requireConfig();
81
86
  const orgId = requireOrg(flags, config, 'myapi people get <person_id> [--org <id>]');
82
87
  requireArg(personId, 'person_id', 'myapi people get <person_id> [--org <id>]');
83
- const res = await sdkPeople.getPerson(config.api_key, orgId, personId);
88
+ const res = await retryFunds(() => sdkPeople.getPerson(config.api_key, orgId, personId));
84
89
  printJson(res);
85
90
  }
86
91
  export const SUBCOMMAND_USAGE = {
@@ -51,7 +51,7 @@ export const SUBCOMMANDS = {
51
51
  people: ['search', 'get'],
52
52
  company: ['search', 'get'],
53
53
  audience: ['create', 'list', 'get', 'update', 'delete', 'members', 'refresh'],
54
- llm: ['complete', 'embed', 'models', 'classify', 'extract', 'summarize', 'draft'],
54
+ llm: ['complete', 'embed', 'models', 'classify', 'extract', 'summarize', 'draft', 'cache'],
55
55
  database: ['namespaces', 'create', 'delete-namespace', 'keys', 'get', 'set', 'del'],
56
56
  crm: ['contacts', 'companies'],
57
57
  url: ['shorten'],
package/dist/flags.d.ts CHANGED
@@ -5,4 +5,4 @@ export interface ParsedArgs {
5
5
  args: string[];
6
6
  flags: Record<string, string | boolean | number>;
7
7
  }
8
- export declare function parseFlags(argv: string[], schema?: FlagSchema): ParsedArgs;
8
+ export declare function parseFlags(argv: string[], schema?: FlagSchema, quiet?: boolean): ParsedArgs;
package/dist/flags.js CHANGED
@@ -12,7 +12,11 @@ export const GLOBAL_FLAGS = {
12
12
  yes: 'boolean',
13
13
  y: 'boolean',
14
14
  };
15
- export function parseFlags(argv, schema = {}) {
15
+ // `quiet` suppresses the unknown-flag note. The dispatcher parses twice — once
16
+ // to learn the command, once under that command's own schema — and only the
17
+ // second pass knows which flags are genuinely foreign, so the first must stay
18
+ // silent or every typo would be reported twice.
19
+ export function parseFlags(argv, schema = {}, quiet = false) {
16
20
  const merged = { ...GLOBAL_FLAGS, ...schema };
17
21
  const args = [];
18
22
  const flags = {};
@@ -126,7 +130,7 @@ export function parseFlags(argv, schema = {}) {
126
130
  // Surface unknown flags so typos and hallucinated flags don't silently
127
131
  // disappear. Don't block — the value is still in `flags` for any handler
128
132
  // that wants it — just print one line on stderr.
129
- if (unknownFlags.length > 0 && !process.env.MYAPI_QUIET_UNKNOWN_FLAGS) {
133
+ if (unknownFlags.length > 0 && !quiet && !process.env.MYAPI_QUIET_UNKNOWN_FLAGS) {
130
134
  process.stderr.write(`› Note: unknown flag(s) not recognized by this command: ${unknownFlags.join(', ')} — check for typos.\n`);
131
135
  }
132
136
  return { args, flags };
package/dist/index.js CHANGED
@@ -158,7 +158,23 @@ async function main() {
158
158
  handleCompletionRequest(); // computes candidates / emits the script, then exits
159
159
  return;
160
160
  }
161
- const { args, flags } = parseFlags(process.argv.slice(2), COMBINED_SCHEMA);
161
+ // Two passes. The first only has to find the command, because the schema
162
+ // that types the flags depends on it: COMBINED_SCHEMA is a merge, and a
163
+ // merge silently resolves collisions by declaration order. `ttl` is declared
164
+ // `number` by domain and llm and `string` by storage, and because storage
165
+ // merges last EVERY --ttl in the CLI was parsed as a string — so
166
+ // `domain records create --ttl 3600` sent "3600" and `llm cache create
167
+ // --ttl 600` dropped the value entirely on a `typeof === 'number'` check.
168
+ //
169
+ // Re-parsing under the command's own schema makes each command's declared
170
+ // types authoritative, which is what every command already assumes, and what
171
+ // warnForeignFlags already assumes when it decides a flag is foreign.
172
+ const first = parseFlags(process.argv.slice(2), COMBINED_SCHEMA, /* quiet */ true);
173
+ const commandName = first.args[0];
174
+ const ownSchema = commandName ? COMMAND_SCHEMAS[commandName] : undefined;
175
+ // The second pass is the one that speaks: it knows the command, so it is the
176
+ // only one that can tell a foreign flag from a flag another command declares.
177
+ const { args, flags } = parseFlags(process.argv.slice(2), ownSchema ?? COMBINED_SCHEMA);
162
178
  warnForeignFlags(args[0], flags);
163
179
  if (flags.version || flags.v || flags.V) {
164
180
  // Read the last-known published version from cache — no network call, so
@@ -1,10 +1,10 @@
1
1
  ---
2
2
  name: my-api-hq
3
- version: 1.2.1
3
+ version: 1.2.2
4
4
  description: >
5
5
  Auth, organizations, and billing hub. Start here to get an api_key and org_id — every other service depends on both.
6
6
  triggers: [api key, account, organization, org, billing, balance, topup, credits, setup, defaults, brand, sync brand, doctor, health check, is my org healthy]
7
- checksum: sha256-b4eef3d505e92f2b9fe8dfa45b74d4221e3c646947ebfe8961aa5a2445bfa139
7
+ checksum: sha256-e7f0a16a130603ca5664cf5c268658bffd85230fe89dc82a4fda715ff81135b8
8
8
  ---
9
9
 
10
10
  # MyApiHQ
@@ -13,7 +13,7 @@ The root service. It manages accounts, API keys, organizations, and billing. No
13
13
 
14
14
  ## Capabilities
15
15
  <!-- llm:start -->
16
- MyApiHQ is the platform's foundation. Every other service (domain, funnel, auth, payments, fn, workflow, database, storage, email, webhook, crm, llm, image, pixel, url) requires both an `api_key` and (for org-scoped resources) an `org_id` minted here. Setup is one command — `myapi account setup` — which provisions an account, generates an api_key, creates a default org, and stores everything in `~/.myapi/config.json`. Subsequent commands pick up those defaults automatically.
16
+ MyApiHQ is the platform's foundation. Every other service requires both an `api_key` and (for org-scoped resources) an `org_id` minted here. Setup is one command — `myapi account setup` — which provisions an account, generates an api_key, creates a default org, and stores everything in `~/.myapi/config.json`. Subsequent commands pick up those defaults automatically.
17
17
 
18
18
  ### Anonymous vs registered accounts
19
19
 
@@ -153,6 +153,7 @@ reply { "success": true, "data": …, "error": null, "meta": {…} }
153
153
 
154
154
  - **Per-slot host** — do not assume one host serves every slot.
155
155
  - **Org id goes in the PATH** — there is no `X-Org-Id` header.
156
+ - **Lists page** — one page is not the whole list; check `meta.has_more`.
156
157
  <!-- http:end -->
157
158
 
158
159
  Run `myapi --help` or `myapi <command> --help` for full flag reference.
@@ -4,7 +4,7 @@ version: 1.1.1
4
4
  description: >
5
5
  Add authentication to apps you build on MyAPI — a managed OIDC identity provider for your app's END USERS (à la Kinde/Auth0). One auth tenant per org; register OIDC clients; sign users in with managed Google or the hosted login page; verify RS256 tokens against the tenant JWKS.
6
6
  triggers: [auth, authentication, login, sign-in, oidc, oauth, jwt, jwks, sso, google sign-in, user accounts, identity provider, kinde, auth0, clerk]
7
- checksum: sha256-ef6a4a0c3b6199fe0335d38b51306301f64bc6e293770700daef625f0deacd33
7
+ checksum: sha256-92ed5cff8e8cd5bed3f59564b3ebb6a66eb572f0914de28322fbda1b8b6ae943
8
8
  ---
9
9
 
10
10
  # MyAuthAPI
@@ -142,4 +142,5 @@ reply { "success": true, "data": …, "error": null, "meta": {…} }
142
142
 
143
143
  - **Per-slot host** — do not assume one host serves every slot.
144
144
  - **Org id goes in the PATH** — there is no `X-Org-Id` header.
145
+ - **Lists page** — one page is not the whole list; check `meta.has_more`.
145
146
  <!-- http:end -->
@@ -1,15 +1,15 @@
1
1
  ---
2
2
  name: my-container-api
3
- version: 1.1.0
3
+ version: 1.1.1
4
4
  description: >
5
5
  Run containers on demand — long-running services, background workers, and scheduled jobs. The heavier-duty sibling of edge functions, for native deps and long execution.
6
6
  triggers: [container, cloud run, dynamic app, custom domain app, service, worker, scheduled job, deploy container, docker image]
7
- checksum: sha256-0a83428114cbc7065d1a4db4f8a0cf39ff965c51acac78f5eeff159345a08da2
7
+ checksum: sha256-80d64f6086e3d16549e530464b8188d9b9db2db5b54696e85c9ddde60d5b40a0
8
8
  ---
9
9
 
10
10
  # MyContainerAPI
11
11
 
12
- A container runs a pre-built image on managed cloud infrastructure. Three types: a **service** (HTTP server, scales to zero), a **worker** (always-on background process), or a **job** (runs to completion — the only type that takes a cron schedule).
12
+ A container runs a pre-built image on managed cloud infrastructure. Three types: a **service** (HTTP server, scales to zero), a **worker** (always-on process), or a **job** (runs to completion — the only type that takes a cron schedule).
13
13
 
14
14
  ## Capabilities
15
15
  <!-- llm:start -->
@@ -55,8 +55,6 @@ it, so the probe would never reach your container.
55
55
 
56
56
  `myapi container domain <id> <domain>` binds a custom domain to a **deployed** container, over HTTPS automatically. The path for a dynamic backend on a real domain — unlike `my-funnel-api`, which serves static sites.
57
57
 
58
- Get it right:
59
-
60
58
  - **Deploy first.** Binding a domain to a container that has never deployed fails (422) — there is nothing running to route to.
61
59
  - **Register the parent domain first** via `my-domain-api`. Binding `app.synthesisdaily.com` requires `synthesisdaily.com` registered in MyAPI; otherwise 422.
62
60
  - **One domain per container.** Re-binding, or binding a hostname already taken, fails (409).
@@ -168,6 +166,7 @@ reply { "success": true, "data": …, "error": null, "meta": {…} }
168
166
 
169
167
  - **Per-slot host** — do not assume one host serves every slot.
170
168
  - **Org id goes in the PATH** — there is no `X-Org-Id` header.
169
+ - **Lists page** — one page is not the whole list; check `meta.has_more`.
171
170
  <!-- http:end -->
172
171
 
173
172
  Run `myapi container --help` for the full flag reference.
@@ -1,10 +1,10 @@
1
1
  ---
2
2
  name: my-crm-api
3
- version: 1.0.1
3
+ version: 1.0.2
4
4
  description: >
5
5
  The canonical store of engaged contacts + companies for an org. Auto-ingests from inbound webhooks via a configurable dot-path. Fixed lifecycle_stage enum (cold | warm | qualified | customer | churned). Append-only event timeline with reserved kinds. Soft delete + restore. Promote-from-Goldfox closes the discovery → engagement loop.
6
6
  triggers: [crm, contact, company, lead, engagement, pipeline, lifecycle, qualified, customer, webhook ingest, promote]
7
- checksum: sha256-285ad94b1266588d103119ec139f11f0d19831bcbfd12779130f9fbeaf7f33f5
7
+ checksum: sha256-ac33bd091d64089bf1d43b20431eff5361e018406bb5046f3c27fd628f7082fb
8
8
  ---
9
9
 
10
10
  # MyCRMAPI
@@ -63,7 +63,7 @@ Coming next (backend wiring in progress):
63
63
 
64
64
  If a contact doesn't exist for the matched email, it's auto-created with `source=` matching the originating service. The contact's company is auto-linked by email domain (creates the company on first sight).
65
65
 
66
- **Missing lead? Check `myapi webhook deliveries` before concluding it never arrived.** A deadlock under concurrent ingest could drop the contact *after* the form returned success (fixed 2026-07-27). Raw payloads are always stored, so the delivery is there even when the contact isn't.
66
+ **Missing lead? Check `myapi webhook deliveries` before concluding it never arrived.** Raw payloads are always stored, so the delivery is there even when the contact isn't.
67
67
 
68
68
  ### Soft delete + restore
69
69
 
@@ -71,7 +71,7 @@ If a contact doesn't exist for the matched email, it's auto-created with `source
71
71
 
72
72
  ### Goldfox enrichment (deferred)
73
73
 
74
- A contact promoted from Goldfox carries a `goldfox_person_id`. In v2 the GET response will embed the row as `goldfox_person`; today it is null. Goldfox-only fields are not yet searchable.
74
+ A contact promoted from Goldfox carries a `goldfox_person_id`; the embedded `goldfox_person` is null today, and Goldfox-only fields are not searchable.
75
75
 
76
76
  ### Search filter — re-engagement semantics
77
77
 
@@ -180,6 +180,7 @@ reply { "success": true, "data": …, "error": null, "meta": {…} }
180
180
 
181
181
  - **Per-slot host** — do not assume one host serves every slot.
182
182
  - **Org id goes in the PATH** — there is no `X-Org-Id` header.
183
+ - **Lists page** — one page is not the whole list; check `meta.has_more`.
183
184
  <!-- http:end -->
184
185
 
185
186
  Run `myapi crm --help` or `myapi crm <namespace> --help` for inline reference.
@@ -4,7 +4,7 @@ version: 1.0.0
4
4
  description: >
5
5
  Per-org KV store with named namespaces, JSON values up to 256 KB, prefix-scan listing, and compare-and-swap via etag. The substrate for any stateful agent-built app on MyAPI — user tables, session stores, idempotency keys, per-user lookup maps.
6
6
  triggers: [database, kv, key value, namespace, store, state, etag, cas, session, idempotency]
7
- checksum: sha256-9b6f14fa5b0115ce042c71f861cb71ec8993aa68447d740b63f5b3689c23b629
7
+ checksum: sha256-91f0ba9f2dba98bcb322951455b153584b667c4570e5536d39dcf10c6cec44c6
8
8
  ---
9
9
 
10
10
  # MyDatabaseAPI
@@ -119,6 +119,7 @@ reply { "success": true, "data": …, "error": null, "meta": {…} }
119
119
 
120
120
  - **Per-slot host** — do not assume one host serves every slot.
121
121
  - **Org id goes in the PATH** — there is no `X-Org-Id` header.
122
+ - **Lists page** — one page is not the whole list; check `meta.has_more`.
122
123
  <!-- http:end -->
123
124
 
124
125
  Run `myapi database --help` for inline reference.
@@ -1,10 +1,10 @@
1
1
  ---
2
2
  name: my-feedback-api
3
- version: 1.7.0
3
+ version: 1.7.1
4
4
  description: >
5
5
  Collect feedback from the people using what you built. A public widget key lets a page submit without a credential; you list, filter and resolve the results. Kind is chosen by the person reporting, not inferred from their wording.
6
6
  triggers: [feedback, bug report, user feedback, feature request, widget, support, complaints, praise]
7
- checksum: sha256-a76c44ae06a9693ec4874132f52675d0344dfdbb174fff668cb602b16dca59b7
7
+ checksum: sha256-144cac696fd3eecf228682929be063a9c26ad3d0f921c41d9ccb433255609c7e
8
8
  ---
9
9
 
10
10
  # MyFeedbackAPI
@@ -75,8 +75,8 @@ probed), `ORIGIN_NOT_ALLOWED`, `RATE_LIMITED`, `INVALID_KIND`, `BODY_REQUIRED`,
75
75
  says it is**. A `bug` means they believe the product is broken — more urgent
76
76
  than a wish for something new. Do not re-classify from the wording.
77
77
 
78
- Classification and duplicate-grouping are designed but not built, so treat
79
- `kind` as the person's label, not a processed signal.
78
+ Nothing classifies or de-duplicates it: `kind` is the person's label, not a
79
+ processed signal.
80
80
 
81
81
  ### Controlling what a report contains
82
82
 
@@ -148,7 +148,7 @@ one, so success is not proof it existed — so ids cannot be probed.
148
148
  | Command | What it does |
149
149
  |---|---|
150
150
  | `myapi feedback create "<text>" --kind <k>` | Record one item (`--page-url`, `--route` for context) |
151
- | `myapi feedback list [--kind bug\|issue\|suggestion] [--status open\|resolved] [--limit N] [--offset N] [--trace]` | List feedback, newest first. Each report summarises what was pointed at, what happened before, and whether a screenshot exists; `--trace` expands the events |
151
+ | `myapi feedback list [--kind bug\|issue\|suggestion] [--status open\|resolved] [--limit N] [--offset N] [--trace]` | List feedback, newest first. Each report summarises what was pointed at and whether a screenshot exists; `--trace` expands the events |
152
152
  | `myapi feedback resolve <id>` | Close an item, keeping it |
153
153
  | `myapi feedback test <id> [--base <url>] [--out <p>]` | Render the report as a Playwright spec (stdout, or `--out`) |
154
154
  | `myapi feedback delete <id>` | Erase an item — for spam, or personal details typed into the box |
@@ -199,6 +199,7 @@ reply { "success": true, "data": …, "error": null, "meta": {…} }
199
199
 
200
200
  - **Per-slot host** — do not assume one host serves every slot.
201
201
  - **Org id goes in the PATH** — there is no `X-Org-Id` header.
202
+ - **Lists page** — one page is not the whole list; check `meta.has_more`.
202
203
  <!-- http:end -->
203
204
 
204
205
  Run `myapi feedback --help` for the full flag reference.
@@ -4,7 +4,7 @@ version: 1.1.0
4
4
  description: >
5
5
  Deploy JavaScript functions to the MyAPI edge runtime. Register a function, upload a single-file JS bundle, get a live HTTP invocation URL or run it on a cron schedule. Each function gets a scoped capability key for cross-slot calls.
6
6
  triggers: [function, deploy function, edge function, serverless, cloudflare worker, cron, scoped api key, capability key, invocation url, bundle]
7
- checksum: sha256-3702cfd630b5ff8a892704c4f6aee9f6678881c73fd3522ce4bd52be865e639e
7
+ checksum: sha256-1e3179c62d2eb1978c6e857e11c7a86c39f3cf784b7de7755ebcb05666b90a17
8
8
  ---
9
9
 
10
10
  # MyFunctionAPI
@@ -159,6 +159,7 @@ reply { "success": true, "data": …, "error": null, "meta": {…} }
159
159
 
160
160
  - **Per-slot host** — do not assume one host serves every slot.
161
161
  - **Org id goes in the PATH** — there is no `X-Org-Id` header.
162
+ - **Lists page** — one page is not the whole list; check `meta.has_more`.
162
163
  <!-- http:end -->
163
164
 
164
165
  Run `myapi fn --help` or `myapi fn <subcommand> --help` for full flag reference.
@@ -1,26 +1,21 @@
1
1
  ---
2
2
  name: my-llm-api
3
- version: 1.2.1
3
+ version: 1.3.0
4
4
  description: >
5
5
  Two-surface LLM primitive. Raw chat completion against self-hosted
6
6
  open-source models (you pick the model), and objective verbs
7
7
  (classify / extract / summarize / draft) that hide the model behind a
8
8
  task. Pricing in cents per 1M tokens; charged from your MyAPI balance.
9
9
  triggers: [llm, completion, chat, embed, embedding, inference, classify, extract, summarize, draft, qwen]
10
- checksum: sha256-d89e5c7ccadb7e967f97ec201b745f2313e4263f8f08a695f4cc9fbaa0c4b500
10
+ checksum: sha256-d63ee7e22984a21313b70af961cee46525c60e7c727372b6c0e2b369c81989d4
11
11
  ---
12
12
 
13
13
  # MyLLMAPI
14
14
 
15
- A two-surface LLM gateway:
16
-
17
- - **Raw** — `complete` / `embed` / `models`. You pick a self-hosted catalog model (run `myapi llm models` for the live list). Proprietary models are not callable here.
18
- - **Verbs** — `classify` / `extract` / `summarize` / `draft`. You ask for a task done; the model is implementation detail.
15
+ A two-surface LLM gateway: **raw** (`complete`/`embed`/`models`) against a self-hosted catalog you pick from, and **verbs** (`classify`/`extract`/`summarize`/`draft`) where the model is implementation detail. Proprietary models are not callable here.
19
16
 
20
17
  ## Capabilities
21
18
  <!-- llm:start -->
22
- For workflow tasks — summarize, classify, extract, draft. The reply goes to stdout and the usage footer (tokens, cost, finish reason) to stderr, so `myapi llm complete ... | jq` works.
23
-
24
19
  **Don't use this as your own model.** If you are an agent reading this, you already have a more capable model. Reach for the LLM verbs when scripting a recurring step where a small, cheap model is the right tool — not for one-shot reasoning you can do yourself.
25
20
 
26
21
  Reach for raw `complete` when shape matters (you build the `messages` array and set `max_tokens`/`temperature`/`stop`); reach for a verb when you want a *result* and don't care which model produced it.
@@ -55,6 +50,26 @@ Roles: `system | user | assistant`. Multiple system messages collapse to one ins
55
50
 
56
51
  If `model` isn't in the self-hosted catalog the server returns `MODEL_NOT_IN_RAW_CATALOG` — that's the signal to use a verb instead, not to retry with a different `--model`.
57
52
 
53
+ ### Context caches — a tenth to re-read
54
+
55
+ Sending the same long prefix on every call (system prompt, reference doc,
56
+ schema)? Store it once and re-read it at a **tenth** the input price. Measured
57
+ on the same 6.9k-token prefix: **0.0073¢ cached vs 0.0629¢ inline.**
58
+
59
+ ```bash
60
+ myapi llm cache create --file ./system-prompt.md --model <id> --ttl 3600
61
+ myapi llm complete "..." --model <id> --cache <cache-id>
62
+ ```
63
+
64
+ - **Bound to ONE model** — another model cannot read it.
65
+ - **It bills for EXISTING, not for being read.** Storage accrues per hour from
66
+ creation whether anyone reads it or not, so `cache delete` is how you stop
67
+ paying; letting the TTL lapse means paying the TTL in full.
68
+ - **`ttl_seconds` is capped at 24h** — silently shortened, not refused. Read
69
+ `expires_at` from the reply rather than trusting your own input.
70
+ - **`usage.cached_input_tokens` is a SUBSET of `input_tokens`**, never an
71
+ addition — summing them double-counts every cached read.
72
+
58
73
  ### Raw `embed`
59
74
 
60
75
  Embed text into a dense vector. `--model` is optional when the catalog serves exactly one embed model; else pass one from `myapi llm models --kind embed`. Returns `EMBED_NOT_AVAILABLE` when none is served.
@@ -63,7 +78,7 @@ Embed text into a dense vector. `--model` is optional when the catalog serves ex
63
78
  - Chat models: `id`, `kind: 'chat'`, `context_window`, `input_cost_per_1m_cents`, `output_cost_per_1m_cents`
64
79
  - Embed models: `id`, `kind: 'embed'`, `dimensions`, `input_cost_per_1m_cents`
65
80
 
66
- The catalog is **live** — it reflects what the inference gateway actually serves, refreshed every 15 minutes. Always query `models` rather than hard-coding ids.
81
+ The catalog is **live**, refreshed every 15 minutes.
67
82
 
68
83
  ### Verb requests + responses
69
84
 
@@ -89,13 +104,8 @@ The model/provider is **never** named in the verb response — the verb is the c
89
104
  `POST /llm/orgs/{org_id}/chat/completions` (alias `/v1/chat/completions`) takes and returns the OpenAI shape — **no envelope**. Same catalog and pricing as `complete`. Use it when an existing OpenAI SDK or LangChain integration should point at MyAPI unchanged.
90
105
 
91
106
  ```python
92
- from openai import OpenAI
93
- client = OpenAI(
94
- api_key="hq_live_…",
95
- base_url="https://api.myapihq.com/llm/orgs/<org_id>/v1",
96
- )
97
- r = client.chat.completions.create(model="<model-id>",
98
- messages=[{"role":"user","content":"Hi"}])
107
+ client = OpenAI(api_key="hq_live_…",
108
+ base_url="https://api.myapihq.com/llm/orgs/<org_id>/v1")
99
109
  ```
100
110
 
101
111
  <!-- llm:end -->
@@ -111,6 +121,7 @@ r = client.chat.completions.create(model="<model-id>",
111
121
  | `myapi llm extract "<input>" --schema <path\|json> [--tier <t>] [--json]` | Pull structured data conforming to a JSON Schema |
112
122
  | `myapi llm summarize "<input>" [--style brief\|exec\|bullet] [--tier <t>] [--json]` | Summarize text |
113
123
  | `myapi llm draft --kind <what> [--prompt "<s>"] [--facts <json>] [--directives <json>] ["<src>"] [--tier <t>] [--json]` | Draft an email / reply / message / … |
124
+ | `myapi llm cache <create --file <p> [--model <id>] [--ttl N] \| list \| delete <id>>` | Store a prompt prefix; re-read it at a tenth the input price |
114
125
  <!-- generated:end -->
115
126
 
116
127
  Pass `-` as the prompt/input to read from stdin. Pass `--file <path>` to read longer content from disk.
@@ -120,24 +131,17 @@ Pass `-` as the prompt/input to read from stdin. Pass `--file <path>` to read lo
120
131
  ```bash
121
132
  # List the live catalog
122
133
  myapi llm models
123
- myapi llm models --kind chat --json | jq '.models[].id'
124
134
 
125
135
  # Raw completion — picks the first chat model from the catalog
126
136
  myapi llm complete "Summarize in 12 words: $(cat README.md)"
127
137
 
128
- # Pin a specific model (ids come from `myapi llm models`)
129
- myapi llm complete "Refactor this function: ..." \
130
- --model <model-id> \
131
- --system "You are a careful Go reviewer." \
132
- --max-tokens 600
133
-
134
138
  # ── Verbs (recommended for workflow steps) ──────────────────────────────
135
139
 
136
140
  myapi llm classify "I was charged twice — please refund." \
137
141
  --labels billing,technical,sales,spam
138
142
 
139
143
  myapi llm extract "Acme Corp employs 250 people in Berlin." \
140
- --schema '{"type":"object","properties":{"company":{"type":"string"},"employees":{"type":"integer"}}}'
144
+ --schema '{"type":"object","properties":{"company":{"type":"string"}}}'
141
145
 
142
146
  myapi llm summarize --file long-thread.txt --style bullet
143
147
 
@@ -154,25 +158,13 @@ INTENT=$(printf '%s' "$BODY" | myapi llm classify - \
154
158
 
155
159
  ## Notes
156
160
 
157
- - **`draft --facts` safety.** Fact values are quoted into the prompt verbatim and sensitive-named keys (`secret`, `api_key`, `password`, …) are NOT redacted. Two guards: injection-defense strips `instructions`/`system`/`prompt`/`override` keys into `meta.warnings`; an output guardrail substring-scans fact values (≥4 chars) and lists hits in `meta.guardrails.facts_in_output` (signal, not redaction). Never put credentials, PII, or internal metadata in `--facts` pass identifiers and reference them indirectly.
161
+ - **`draft --facts` safety.** Fact values are quoted verbatim and sensitive-named keys are NOT redacted. Instruction-like keys are stripped into `meta.warnings`, and fact values echoed in the output are listed in `meta.guardrails.facts_in_output` signal, not redaction. Never put credentials or PII in `--facts`; pass identifiers and reference them indirectly.
162
+ - **`--facts` vs `--directives`.** Facts are referent data (recipient, dates, amounts), quoted as reference and never as instructions; directives are writer controls only (tone, max_words, format). They are trusted differently. `--context` is the old name for `--facts`.
158
163
  - **`402`** — `INSUFFICIENT_FUNDS`: top up or enable `myapi billing auto-recharge`. `SPEND_CAP_EXCEEDED`: raise your own ceiling with `myapi billing spend-cap`.
159
164
  - **Self-hosted raw, server-picked verbs.** Raw runs on MyAPI's TPU; verbs route wherever the server picks.
160
165
  - **Cost + latency.** `usage.cost_cents` is authoritative — no markup. Varies by tier: 200–600 ms to first token, 1–3 s end-to-end.
161
166
  - **Live catalog, no streaming, no BYOK.** Don't hard-code ids — `models` is truth (CLI auto-picks if `--model` omitted). Full reply only.
162
167
 
163
- ## `--facts` vs `--directives` on draft
164
-
165
- `--facts '<json>'` is referent data, quoted as reference and never as
166
- instructions (recipient, dates, amounts). `--directives '<json>'` is writer
167
- controls only: tone, max_words, format, style. They are trusted differently.
168
-
169
- ```bash
170
- myapi llm draft --kind email --prompt "the invoice is due" \
171
- --facts '{"to":"Ada"}' --directives '{"tone":"warm"}'
172
- ```
173
-
174
- `--context` is the old name for `--facts`; accepted, deprecated upstream.
175
-
176
168
  ## HTTP (from deployed code)
177
169
 
178
170
  <!-- http:start -->
@@ -4,7 +4,7 @@ version: 1.0.1
4
4
  description: >
5
5
  Durable job queue — enqueue work and have it retried against your HTTP consumer, with concurrency caps and a dependency DAG.
6
6
  triggers: [queue, job queue, background job, enqueue, retry, async work, dead letter, delayed job, concurrency]
7
- checksum: sha256-ad874e1229df9381fc92881b2a1fada40859983c24fe4386d43922398323b05c
7
+ checksum: sha256-762f5f68386044a4313935f14643c8cbd784e7c6a4133308d0f14658062c9a0b
8
8
  ---
9
9
 
10
10
  # MyQueueAPI
@@ -89,6 +89,7 @@ reply { "success": true, "data": …, "error": null, "meta": {…} }
89
89
 
90
90
  - **Per-slot host** — do not assume one host serves every slot.
91
91
  - **Org id goes in the PATH** — there is no `X-Org-Id` header.
92
+ - **Lists page** — one page is not the whole list; check `meta.has_more`.
92
93
  <!-- http:end -->
93
94
 
94
95
  ## workflow vs queue vs task
@@ -4,7 +4,7 @@ version: 1.1.0
4
4
  description: >
5
5
  Edge-hosted asset storage. Upload a local file of any content type directly, or have the server fetch from a public URL. Each asset gets a stable public CDN URL.
6
6
  triggers: [storage, upload, ingest, asset, cdn, image hosting, file upload, get-url, download, public url]
7
- checksum: sha256-1452c30c103d0bb8b76a8f6d8e1688cadd556922b8c60e7ac754222c33b65524
7
+ checksum: sha256-ef0154ed196082be84192c7ddfb4cab47641549ed9703087c9eb4643daea678a
8
8
  ---
9
9
 
10
10
  # MyStorageAPI
@@ -134,6 +134,7 @@ reply { "success": true, "data": …, "error": null, "meta": {…} }
134
134
 
135
135
  - **Per-slot host** — do not assume one host serves every slot.
136
136
  - **Org id goes in the PATH** — there is no `X-Org-Id` header.
137
+ - **Lists page** — one page is not the whole list; check `meta.has_more`.
137
138
  <!-- http:end -->
138
139
 
139
140
  Run `myapi storage --help` for full flag reference.
@@ -4,7 +4,7 @@ version: 1.0.1
4
4
  description: >
5
5
  Inbound webhook endpoints for non-funnel sources — Stripe, GitHub, custom services. Funnel forms use the my-funnel-api proxy.
6
6
  triggers: [webhook, inbound, receiver, stripe events, github webhook, slack notification, delivery, payload, event ingest]
7
- checksum: sha256-737e0572ef7c414dab568e60af676aa8e50900ed368ba7bcdf81fc65516be9eb
7
+ checksum: sha256-aff831f651b965cc48fd26bbd20c04bd44443a2073917a4660aefaffc5091dcd
8
8
  ---
9
9
 
10
10
  # MyWebhookAPI
@@ -127,6 +127,7 @@ reply { "success": true, "data": …, "error": null, "meta": {…} }
127
127
 
128
128
  - **Per-slot host** — do not assume one host serves every slot.
129
129
  - **Org id goes in the PATH** — there is no `X-Org-Id` header.
130
+ - **Lists page** — one page is not the whole list; check `meta.has_more`.
130
131
  <!-- http:end -->
131
132
 
132
133
  Run `myapi webhook --help` for full flag reference.
@@ -4,7 +4,7 @@ version: 1.0.0
4
4
  description: >
5
5
  Run actions when a webhook fires. Trigger emails, Slack notifications, or HTTP calls in response to inbound webhook deliveries — without writing a backend.
6
6
  triggers: [workflow, automation, on webhook, send email on, slack notification, payload templating, drip, trigger, run]
7
- checksum: sha256-35b09cb26aa3781c78abdc2b362bdb8f0817a9580467bf94e65e0f2f335d502a
7
+ checksum: sha256-0a1607bd1a89cd13a052b426fa2262cc1cef71c52acf8c63fc7e8026ba6ba52c
8
8
  ---
9
9
 
10
10
  # MyWorkflowAPI
@@ -128,6 +128,7 @@ reply { "success": true, "data": …, "error": null, "meta": {…} }
128
128
 
129
129
  - **Per-slot host** — do not assume one host serves every slot.
130
130
  - **Org id goes in the PATH** — there is no `X-Org-Id` header.
131
+ - **Lists page** — one page is not the whole list; check `meta.has_more`.
131
132
  <!-- http:end -->
132
133
 
133
134
  Run `myapi workflow --help` for full flag reference.
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@myapihq/cli",
3
3
  "license": "Apache-2.0",
4
- "version": "2.20.1",
4
+ "version": "2.21.1",
5
5
  "description": "MyAPI command-line interface",
6
6
  "repository": {
7
7
  "type": "git",
@@ -46,7 +46,7 @@
46
46
  "lint:skills:strict": "node scripts/copy-skills.js && node scripts/lint-skills.js --strict"
47
47
  },
48
48
  "dependencies": {
49
- "@myapihq/sdk": "^2.20.1"
49
+ "@myapihq/sdk": "^2.21.1"
50
50
  },
51
51
  "devDependencies": {
52
52
  "@types/node": "^25.6.0",