@myapihq/cli 2.4.2 → 2.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.
Files changed (49) hide show
  1. package/dist/commands/authproduct.js +1 -1
  2. package/dist/commands/billing.js +8 -2
  3. package/dist/commands/config.js +16 -0
  4. package/dist/commands/container.d.ts +2 -1
  5. package/dist/commands/container.js +30 -5
  6. package/dist/commands/fn.d.ts +1 -1
  7. package/dist/commands/fn.js +51 -9
  8. package/dist/commands/keys.js +5 -0
  9. package/dist/commands/login.d.ts +1 -0
  10. package/dist/commands/login.js +62 -2
  11. package/dist/commands/payments.js +6 -2
  12. package/dist/commands/pixel.d.ts +2 -1
  13. package/dist/commands/pixel.js +28 -1
  14. package/dist/commands/queue.d.ts +1 -1
  15. package/dist/commands/queue.js +15 -2
  16. package/dist/commands/setup.js +6 -2
  17. package/dist/commands/storage.js +13 -2
  18. package/dist/commands/webhook.d.ts +1 -0
  19. package/dist/commands/webhook.js +74 -2
  20. package/dist/errors.js +6 -0
  21. package/dist/flags.d.ts +1 -1
  22. package/dist/flags.js +21 -0
  23. package/dist/flags.test.js +35 -0
  24. package/dist/index.js +60 -0
  25. package/dist/output.js +8 -0
  26. package/dist/sdk-container.test.js +5 -1
  27. package/dist/skills/my-audience-api/README.md +1 -1
  28. package/dist/skills/my-auth-api/SKILL.md +3 -1
  29. package/dist/skills/my-company-api/README.md +1 -1
  30. package/dist/skills/my-container-api/README.md +1 -1
  31. package/dist/skills/my-container-api/SKILL.md +3 -1
  32. package/dist/skills/my-crm-api/README.md +1 -1
  33. package/dist/skills/my-crm-api/SKILL.md +6 -4
  34. package/dist/skills/my-database-api/README.md +1 -1
  35. package/dist/skills/my-database-api/SKILL.md +3 -1
  36. package/dist/skills/my-email-api/README.md +1 -1
  37. package/dist/skills/my-email-verify-api/README.md +1 -1
  38. package/dist/skills/my-git-api/README.md +1 -1
  39. package/dist/skills/my-image-api/README.md +1 -1
  40. package/dist/skills/my-llm-api/README.md +1 -1
  41. package/dist/skills/my-people-api/README.md +1 -1
  42. package/dist/skills/my-pixel-api/README.md +1 -1
  43. package/dist/skills/my-queue-api/README.md +1 -1
  44. package/dist/skills/my-storage-api/README.md +1 -1
  45. package/dist/skills/my-task-api/README.md +1 -1
  46. package/dist/skills/my-webhook-api/README.md +1 -1
  47. package/dist/skills/my-webhook-api/SKILL.md +7 -3
  48. package/dist/skills/my-workflow-api/README.md +1 -1
  49. package/package.json +3 -2
@@ -2,12 +2,14 @@ import { webhook as sdkWebhook } 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 } from '../helpers.js';
5
+ import { formatDate } from '../utils.js';
5
6
  export const EXPOSES = [
6
7
  'POST /webhook/orgs/{org_id}/endpoints',
7
8
  'GET /webhook/orgs/{org_id}/endpoints',
8
9
  'PATCH /webhook/orgs/{org_id}/endpoints/{endpoint_id}',
9
10
  'DELETE /webhook/orgs/{org_id}/endpoints/{endpoint_id}',
10
11
  'GET /webhook/orgs/{org_id}/deliveries/{delivery_id}',
12
+ 'GET /webhook/orgs/{org_id}/deliveries',
11
13
  ];
12
14
  export const SCHEMA = {
13
15
  org: 'string',
@@ -16,6 +18,9 @@ export const SCHEMA = {
16
18
  'crm-email-path': 'string',
17
19
  // 2026-05-15: backend's POST/PATCH endpoints accept forward_url.
18
20
  'forward-url': 'string',
21
+ 'endpoint-id': 'string',
22
+ limit: 'number',
23
+ cursor: 'string',
19
24
  };
20
25
  export async function list(flags) {
21
26
  const config = requireConfig();
@@ -130,6 +135,62 @@ export async function delivery(id, flags) {
130
135
  const res = await sdkWebhook.getDelivery(config.api_key, orgId, id);
131
136
  printJson(res);
132
137
  }
138
+ // deliveries — what an endpoint actually received.
139
+ //
140
+ // Before this existed, `delivery <id>` was the only read and the id is only
141
+ // ever handed to whoever POSTed. For a funnel form that is the visitor's
142
+ // browser, so the agent that built the form could not see its own
143
+ // submissions; CRM ingest (email only) was the sole alternative and every
144
+ // other field was unreachable. This is that gap closed.
145
+ export async function deliveries(endpointArg, flags) {
146
+ const config = requireConfig();
147
+ const orgId = requireOrg(flags, config, 'myapi webhook deliveries [endpoint_id] [--limit N] [--cursor <c>] [--org <id>]');
148
+ const endpointId = endpointArg ?? flags['endpoint-id'];
149
+ const page = await sdkWebhook.listDeliveries(config.api_key, orgId, {
150
+ endpointId,
151
+ limit: typeof flags.limit === 'number' ? flags.limit : undefined,
152
+ cursor: typeof flags.cursor === 'string' ? flags.cursor : undefined,
153
+ });
154
+ if (flags.json) {
155
+ printJson(page);
156
+ return;
157
+ }
158
+ if (page.deliveries.length === 0) {
159
+ info(endpointId
160
+ ? `No deliveries yet for endpoint ${endpointId}.`
161
+ : 'No deliveries yet in this org. POST to an endpoint\'s inbound URL to create one.');
162
+ return;
163
+ }
164
+ printTable(page.deliveries.map(d => ({
165
+ id: d.id,
166
+ endpoint_id: d.endpoint_id,
167
+ received_at: formatDate(d.received_at),
168
+ forward: d.forward_status !== undefined ? String(d.forward_status) : '',
169
+ payload: summarizePayload(d.payload),
170
+ })), { flags });
171
+ // The full payload is the point of this command, so say how to get it
172
+ // rather than leaving the summary column as the only view.
173
+ info('');
174
+ info('Full payload for one delivery: myapi webhook delivery <id>');
175
+ if (page.next_cursor) {
176
+ info(`More available — next page: myapi webhook deliveries --cursor ${page.next_cursor}`);
177
+ }
178
+ }
179
+ // One-line preview of an arbitrary JSON payload. The table is for scanning;
180
+ // `webhook delivery <id>` is for reading.
181
+ function summarizePayload(payload) {
182
+ if (payload === null || payload === undefined)
183
+ return '';
184
+ if (typeof payload !== 'object')
185
+ return String(payload).slice(0, 60);
186
+ const entries = Object.entries(payload);
187
+ if (entries.length === 0)
188
+ return '{}';
189
+ const shown = entries.slice(0, 3)
190
+ .map(([k, v]) => `${k}=${typeof v === 'object' ? '…' : String(v).slice(0, 20)}`)
191
+ .join(' ');
192
+ return entries.length > 3 ? `${shown} +${entries.length - 3}` : shown;
193
+ }
133
194
  // ── Dispatcher ───────────────────────────────────────────────────────────────
134
195
  const SUBCOMMAND_USAGE = {
135
196
  'list': 'myapi webhook list [--org <id>] [--json]',
@@ -153,6 +214,11 @@ endpoint).
153
214
  Slug is immutable; create a new endpoint if you need a different slug.`,
154
215
  'delete': 'myapi webhook delete <id> [--org <id>]',
155
216
  'delivery': 'myapi webhook delivery <delivery_id> [--org <id>]',
217
+ 'deliveries': `myapi webhook deliveries [endpoint_id] [--limit N] [--cursor <c>] [--org <id>] [--json]
218
+
219
+ Lists what your endpoints actually received, newest first. Use this rather
220
+ than trying to capture delivery ids at POST time — for a funnel form the id
221
+ goes to the visitor's browser, not to you.`,
156
222
  };
157
223
  export async function run(subcommand, args, flags) {
158
224
  if (!subcommand || (flags.help && !subcommand)) {
@@ -161,6 +227,7 @@ export async function run(subcommand, args, flags) {
161
227
  Subcommands:
162
228
  create Create a new endpoint to receive data (returns an inbound URL)
163
229
  delete Delete a webhook endpoint
230
+ deliveries List what your endpoints received (newest first, paginated)
164
231
  delivery Inspect a specific webhook delivery (payload, received_at)
165
232
  list List all inbound webhook endpoints
166
233
  update Patch endpoint name / description / crm-email-path / forward-url
@@ -173,8 +240,12 @@ Payload contract:
173
240
  Workflows reference fields with {{ payload.field }} templating.
174
241
 
175
242
  Discovering delivery IDs:
176
- Each POST to an inbound URL responds with {"delivery_id":"<uuid>"}.
177
- Capture it from the response, then pass to: myapi webhook delivery <id>
243
+ myapi webhook deliveries — list what came in, newest first
244
+ myapi webhook delivery <id> — full payload for one
245
+
246
+ A POST to an inbound URL does return {"delivery_id":"<uuid>"}, but for a
247
+ funnel form that response goes to the visitor's browser, not to you — so
248
+ list them rather than trying to capture ids at submit time.
178
249
 
179
250
  Example — wire a contact form to email + Slack:
180
251
  WID=$(myapi webhook create "contact-form" --json | jq -r .id)
@@ -199,6 +270,7 @@ See the my-webhook-api skill for the full HTML + JS recipe.`);
199
270
  case 'update': return update(args[0], flags);
200
271
  case 'delete': return del(args[0], flags);
201
272
  case 'delivery': return delivery(args[0], flags);
273
+ case 'deliveries': return deliveries(args[0], flags);
202
274
  default: error(`Unknown subcommand: ${subcommand}. Run "myapi webhook --help" for a list of valid subcommands.`);
203
275
  }
204
276
  }
package/dist/errors.js CHANGED
@@ -28,6 +28,12 @@ export const ERROR_MESSAGES = {
28
28
  INSUFFICIENT_BALANCE: 'Insufficient balance. Run: myapi billing topup <amount>',
29
29
  INVALID_AMOUNT: 'Amount out of range. Maximum single top-up is $100. Run: myapi billing topup <amount>',
30
30
  SERVICE_NOT_LAUNCHED: 'This service is disabled pre-launch. Track availability via: myapi status',
31
+ // Every other blocker in the CLI names the command that unblocks it
32
+ // (DOMAIN_NOT_OWNED → `domain register`, MAILBOX_NOT_OWNED → …). This one
33
+ // stated the fact and stopped, so an agent had to go read `database --help`
34
+ // to discover namespaces are created explicitly. Observed during the
35
+ // 2026-07-27 from-scratch run.
36
+ NAMESPACE_NOT_FOUND: 'Namespace not found. Namespaces are created explicitly — run: myapi database create <name> (list them with: myapi database namespaces)',
31
37
  RECORD_NOT_FOUND: 'DNS record not found in this zone.',
32
38
  INVALID_RECORD_TYPE: 'Unsupported record type. Allowed: A, AAAA, CNAME, MX, TXT.',
33
39
  MX_PRIORITY_REQUIRED: 'MX records require --priority (typical value: 10).',
package/dist/flags.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- export type FlagType = 'string' | 'boolean' | 'number';
1
+ export type FlagType = 'string' | 'boolean' | 'number' | 'list';
2
2
  export type FlagSchema = Record<string, FlagType>;
3
3
  export declare const GLOBAL_FLAGS: FlagSchema;
4
4
  export interface ParsedArgs {
package/dist/flags.js CHANGED
@@ -17,6 +17,7 @@ export function parseFlags(argv, schema = {}) {
17
17
  const args = [];
18
18
  const flags = {};
19
19
  const unknownFlags = [];
20
+ const seenValueFlags = new Set();
20
21
  for (let i = 0; i < argv.length; i++) {
21
22
  const arg = argv[i];
22
23
  if (arg === '-h') {
@@ -91,6 +92,26 @@ export function parseFlags(argv, schema = {}) {
91
92
  flags[key] = true;
92
93
  continue;
93
94
  }
95
+ // Repeatable flag: accumulate rather than overwrite.
96
+ if (type === 'list') {
97
+ const prev = flags[key];
98
+ flags[key] = typeof prev === 'string' && prev.length > 0 ? `${prev},${raw}` : raw;
99
+ continue;
100
+ }
101
+ // A value flag given twice used to keep the LAST occurrence, silently.
102
+ // Reported from the field (Skout, 2026-07-27): three `--redirect` flags
103
+ // on `auth client create` registered one URI, reported success, and the
104
+ // production redirect was the one dropped — sign-in would have worked in
105
+ // dev and failed only for real users. Repeating a flag is a natural
106
+ // mistake when the docs say "comma-separated"; silently honouring the
107
+ // last is the one behaviour that can't be recovered from. Refuse, and
108
+ // say what to do instead.
109
+ if (seenValueFlags.has(key)) {
110
+ throw new Error(`--${key} was given more than once. Only the last value would be used, ` +
111
+ `which silently discards the others.\n` +
112
+ `→ If you meant to pass several values, comma-separate them: --${key} a,b,c`);
113
+ }
114
+ seenValueFlags.add(key);
94
115
  if (type === 'number') {
95
116
  const n = Number(raw);
96
117
  flags[key] = isNaN(n) ? raw : n;
@@ -155,3 +155,38 @@ describe('parseFlags', () => {
155
155
  expect(flags['no-enable']).toBe(true);
156
156
  });
157
157
  });
158
+ // Repeated flags. Reported from the field (Skout, 2026-07-27): three
159
+ // `--redirect` flags on `auth client create` registered ONE URI and reported
160
+ // success. The dropped one was production, so sign-in worked in dev and would
161
+ // have failed only for real users. The reporter caught it solely by reading
162
+ // the confirmation line.
163
+ describe('parseFlags — repeated flags are never silently collapsed', () => {
164
+ it('accumulates a repeatable (list) flag instead of keeping the last', () => {
165
+ const { flags } = parseFlags(['--redirect', 'https://prod/cb', '--redirect', 'http://localhost:5173/cb'], { redirect: 'list' });
166
+ expect(flags.redirect).toBe('https://prod/cb,http://localhost:5173/cb');
167
+ });
168
+ it('still accepts the documented comma-separated form for a list flag', () => {
169
+ const { flags } = parseFlags(['--redirect', 'a,b,c'], { redirect: 'list' });
170
+ expect(flags.redirect).toBe('a,b,c');
171
+ });
172
+ it('mixes repeated and comma forms', () => {
173
+ const { flags } = parseFlags(['--redirect', 'a,b', '--redirect', 'c'], { redirect: 'list' });
174
+ expect(flags.redirect).toBe('a,b,c');
175
+ });
176
+ it('REFUSES a non-repeatable value flag given twice rather than keeping the last', () => {
177
+ expect(() => parseFlags(['--name', 'prod', '--name', 'dev'], { name: 'string' }))
178
+ .toThrow(/given more than once/);
179
+ });
180
+ it('names the comma-separated escape hatch in the refusal', () => {
181
+ expect(() => parseFlags(['--name', 'a', '--name', 'b'], { name: 'string' }))
182
+ .toThrow(/comma-separate them/);
183
+ });
184
+ it('does not refuse repeated boolean flags — harmless and idempotent', () => {
185
+ const { flags } = parseFlags(['--yes', '--yes'], {});
186
+ expect(flags.yes).toBe(true);
187
+ });
188
+ it('a single occurrence of a value flag is unaffected', () => {
189
+ const { flags } = parseFlags(['--name', 'prod'], { name: 'string' });
190
+ expect(flags.name).toBe('prod');
191
+ });
192
+ });
package/dist/index.js CHANGED
@@ -47,6 +47,65 @@ import * as loginCmd from './commands/login.js';
47
47
  // Each command file declares the value flags it understands. We union them
48
48
  // into a single schema for the upfront parse, so adding a new value flag in
49
49
  // one command means editing one file (its SCHEMA), not a global allowlist.
50
+ // Which command owns which flag schema. parseFlags necessarily runs against
51
+ // the UNION of every schema (it has to know whether `--foo` eats the next
52
+ // token before it knows the command), and its unknown-flag warning therefore
53
+ // only fires for a flag no command declares. The consequence, found by
54
+ // building a company from scratch on 2026-07-27: `myapi task create "x"
55
+ // --priority 9` was accepted in silence and the value discarded, because
56
+ // `priority` is a real flag — on `domain`, for MX records. The agent gets a
57
+ // success and a task with default importance.
58
+ //
59
+ // This map lets us re-check the flags against the schema of the command that
60
+ // actually ran, so a flag borrowed from a sibling is called out the same way a
61
+ // typo already was. Commands absent from the map (setup, update) are skipped
62
+ // rather than guessed at.
63
+ const COMMAND_SCHEMAS = {
64
+ account: accountCmd.SCHEMA,
65
+ audience: audienceCmd.SCHEMA,
66
+ auth: authProductCmd.SCHEMA,
67
+ billing: billingCmd.SCHEMA,
68
+ company: companyCmd.SCHEMA,
69
+ config: configCmd.SCHEMA,
70
+ container: containerCmd.SCHEMA,
71
+ crm: crmCmd.SCHEMA,
72
+ database: databaseCmd.SCHEMA,
73
+ doctor: doctorCmd.SCHEMA,
74
+ domain: domainCmd.SCHEMA,
75
+ email: emailCmd.SCHEMA,
76
+ fn: fnCmd.SCHEMA,
77
+ funnel: funnelCmd.SCHEMA,
78
+ git: gitCmd.SCHEMA,
79
+ image: imageCmd.SCHEMA,
80
+ keys: keysCmd.SCHEMA,
81
+ llm: llmCmd.SCHEMA,
82
+ login: loginCmd.SCHEMA,
83
+ org: orgCmd.SCHEMA,
84
+ payments: paymentsCmd.SCHEMA,
85
+ people: peopleCmd.SCHEMA,
86
+ pixel: pixelCmd.SCHEMA,
87
+ queue: queueCmd.SCHEMA,
88
+ status: statusCmd.SCHEMA,
89
+ storage: storageCmd.SCHEMA,
90
+ task: taskCmd.SCHEMA,
91
+ url: urlCmd.SCHEMA,
92
+ webhook: webhookCmd.SCHEMA,
93
+ workflow: workflowCmd.SCHEMA,
94
+ };
95
+ // Flags the dispatcher itself consumes, valid on any command.
96
+ const DISPATCH_FLAGS = new Set(['help', 'h', 'json', 'verbose', 'yes', 'y', 'version', 'v', 'V', 'org']);
97
+ function warnForeignFlags(command, flags) {
98
+ if (!command || process.env.MYAPI_QUIET_UNKNOWN_FLAGS)
99
+ return;
100
+ const schema = COMMAND_SCHEMAS[command];
101
+ if (!schema)
102
+ return; // command has no declared schema — nothing to compare against
103
+ const foreign = Object.keys(flags).filter(k => !DISPATCH_FLAGS.has(k) && !(k in schema));
104
+ if (foreign.length === 0)
105
+ return;
106
+ banner(`› Note: ${foreign.map(f => `--${f}`).join(', ')} ${foreign.length === 1 ? 'is' : 'are'} not a flag of \`myapi ${command}\` — ` +
107
+ `the value was ignored. Run \`myapi ${command} --help\`.`);
108
+ }
50
109
  const COMBINED_SCHEMA = {
51
110
  ...accountCmd.SCHEMA,
52
111
  ...authProductCmd.SCHEMA,
@@ -92,6 +151,7 @@ async function main() {
92
151
  return;
93
152
  }
94
153
  const { args, flags } = parseFlags(process.argv.slice(2), COMBINED_SCHEMA);
154
+ warnForeignFlags(args[0], flags);
95
155
  if (flags.version || flags.v || flags.V) {
96
156
  // Read the last-known published version from cache — no network call, so
97
157
  // `myapi --version` stays instant. The cache is refreshed by the
package/dist/output.js CHANGED
@@ -26,6 +26,14 @@ export function spinnerFrame(i) {
26
26
  return SPINNER_FRAMES[i % SPINNER_FRAMES.length];
27
27
  }
28
28
  export function spinnerWrite(s) {
29
+ // Spinner frames rely on \r overwriting the line, which only happens on a
30
+ // terminal. Piped or captured — which is how every agent runs this — each
31
+ // frame just accumulates, so a single image generation produced
32
+ // "Generating image ⠋Generating image ⠙Generating image ⠹…" in the output an
33
+ // agent then has to read. Progress animation is for humans; suppress it
34
+ // when nobody is watching.
35
+ if (!process.stderr.isTTY)
36
+ return;
29
37
  process.stderr.write(s);
30
38
  }
31
39
  export function clearLine() {
@@ -126,7 +126,7 @@ describe('container.getContainerLogs', () => {
126
126
  });
127
127
  });
128
128
  describe('container.EXPOSES', () => {
129
- it('covers the 8 container endpoints', () => {
129
+ it('covers the 9 container endpoints', () => {
130
130
  expect(container.EXPOSES).toEqual([
131
131
  'POST /container/orgs/{org_id}/containers',
132
132
  'GET /container/orgs/{org_id}/containers',
@@ -136,6 +136,10 @@ describe('container.EXPOSES', () => {
136
136
  'GET /container/orgs/{org_id}/containers/{id}/logs',
137
137
  'POST /container/orgs/{org_id}/containers/{id}/domain',
138
138
  'DELETE /container/orgs/{org_id}/containers/{id}/domain',
139
+ // Added 2026-07-27: build-logs answers WHY a --source build failed,
140
+ // separately from the runtime `logs` stream (which interleaves platform
141
+ // audit records with application output).
142
+ 'GET /container/orgs/{org_id}/containers/{id}/build-logs',
139
143
  ]);
140
144
  });
141
145
  });
@@ -28,7 +28,7 @@ myapi audience refresh <id>
28
28
  ## Authentication
29
29
 
30
30
  ```bash
31
- export MYAPI_KEY=mak_...
31
+ export MYAPI_KEY=hq_live_...
32
32
  ```
33
33
 
34
34
  Requires `api_key` and `org_id` from **myapihq**.
@@ -4,7 +4,7 @@ version: 1.0.0
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-11dac8e8da70bd6bbf63fe11fae5adc038584dc23a09350b9e7b977a669aed41
7
+ checksum: sha256-b5eca82bb59647677fdb5796778329160572598ac6914887768fd33529cdfe84
8
8
  ---
9
9
 
10
10
  # MyAuthAPI
@@ -113,3 +113,5 @@ myapi auth client list
113
113
  - Redirect URIs are matched exactly: absolute `https://…` (or
114
114
  `http://localhost…` for local dev).
115
115
  - `402 INSUFFICIENT_FUNDS` = empty wallet → `myapi billing topup <amount>` (or keep it funded automatically: `myapi billing auto-recharge set`). `402 SPEND_CAP_EXCEEDED` = you hit your account spend ceiling → raise it with `myapi billing spend-cap`.
116
+
117
+ **End-to-end example:** `examples/authenticated-app/` walks the full seam — hosted login → token verification → per-user KV record → deployed container — including the parts that cost real users hours (verify the id_token for identity; the access token is a bearer credential for `<issuer>/userinfo`).
@@ -23,7 +23,7 @@ myapi company get company_06 --include-people 5
23
23
  ## Authentication
24
24
 
25
25
  ```bash
26
- export MYAPI_KEY=mak_...
26
+ export MYAPI_KEY=hq_live_...
27
27
  ```
28
28
 
29
29
  Requires `api_key` and `org_id` from **myapihq**.
@@ -22,7 +22,7 @@ myapi container domain <id> app.yourbrand.com
22
22
  ## Authentication
23
23
 
24
24
  ```bash
25
- export MYAPI_KEY=mak_...
25
+ export MYAPI_KEY=hq_live_...
26
26
  ```
27
27
 
28
28
  Requires `api_key` + `org_id` from **myapihq**. Custom domains require the parent domain registered via **mydomainapi**.
@@ -4,7 +4,7 @@ version: 1.0.0
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-ce30765b31f9c29012dcfc13b538cf47bb90e21bc015b4cfc75c1e55b21b4d75
7
+ checksum: sha256-0bd2fdee306f944702faf18b0cbb5d976a2bdc39c1f940c037600b0c0f6a4691
8
8
  ---
9
9
 
10
10
  # MyContainerAPI
@@ -75,3 +75,5 @@ myapi container domain <id> --remove
75
75
  - Containers are for dynamic apps and native deps. For static sites use `my-funnel-api`; for edge functions use `my-function-api`.
76
76
 
77
77
  Run `myapi container --help` for the full flag reference.
78
+
79
+ **End-to-end example:** `examples/authenticated-app/` walks the full seam — hosted login → token verification → per-user KV record → deployed container — including the parts that cost real users hours (env vars are create-only; a service is multi-instance; health-check `/livez` not `/healthz`).
@@ -30,7 +30,7 @@ myapi crm contacts events <id>
30
30
  ## Authentication
31
31
 
32
32
  ```bash
33
- export MYAPI_KEY=mak_...
33
+ export MYAPI_KEY=hq_live_...
34
34
  ```
35
35
 
36
36
  Requires `api_key` and `org_id` from **myapihq**.
@@ -4,7 +4,7 @@ version: 1.0.0
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-0ee12d579c51b300ff227a094c2082a2c6f3bf83d2d97ab16cb2f47ad3667fd6
7
+ checksum: sha256-1e1e7a8c4674f7a109f5bb0b5e9f053e938d2ee36d56c6ce8a4276826f7d9609
8
8
  ---
9
9
 
10
10
  # MyCRMAPI
@@ -38,7 +38,7 @@ Move stage with `myapi crm contacts update <id> --stage qualified`. Every stage
38
38
  goldfox | email | pixel | webhook | manual
39
39
  ```
40
40
 
41
- Set automatically based on how the contact entered the CRM. Useful filter when you want "only contacts I've personally added" (`--source manual`) vs "warmed via Goldfox outreach" (`--source goldfox`).
41
+ Set automatically from how the contact entered. Filter with `--source manual` (added by hand) vs `--source goldfox` (from outreach).
42
42
 
43
43
  ### Event timeline — reserved kinds
44
44
 
@@ -48,9 +48,9 @@ email_sent | email_opened | email_clicked | email_replied
48
48
  pixel_visit | webhook_received | payment
49
49
  ```
50
50
 
51
- Agents cannot write events directly — the closed enum is intentional. If you need custom state, use **mydatabaseapi** (KV) keyed on the contact id; the curated timeline stays authoritative for engagement.
51
+ Agents cannot write events directly — the closed enum is intentional. For custom state use **mydatabaseapi** (KV) keyed on the contact id; the curated timeline stays authoritative.
52
52
 
53
- **Engagement-event kinds bump `last_engagement_at`**: email_sent/opened/clicked/replied, pixel_visit, webhook_received. Admin kinds (created, promoted, stage_changed) don't — promoting a Goldfox lead isn't engagement.
53
+ **Engagement kinds bump `last_engagement_at`**: email_*, pixel_visit, webhook_received. Admin kinds (created, promoted, stage_changed) don't — promoting a lead isn't engagement.
54
54
 
55
55
  ### Auto-ingest
56
56
 
@@ -63,6 +63,8 @@ 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 to the visitor (fixed 2026-07-27). Raw payloads were always stored, so the delivery is there even when the contact isn't.
67
+
66
68
  ### Soft delete + restore
67
69
 
68
70
  `myapi crm contacts delete <id>` sets `deleted_at` but **retains the event timeline**. By default soft-deleted contacts are excluded from search — pass `--include-deleted` to see them. Restore with `myapi crm contacts restore <id>`.
@@ -21,7 +21,7 @@ myapi database keys --ns my-app --values
21
21
  ## Authentication
22
22
 
23
23
  ```bash
24
- export MYAPI_KEY=mak_...
24
+ export MYAPI_KEY=hq_live_...
25
25
  ```
26
26
 
27
27
  Requires `api_key` and `org_id` from **myapihq**.
@@ -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-247b0efe4af6d1eebc320a11b9b7ce3f5b1aa6a2f86fa80631bb3fec1e0a8d22
7
+ checksum: sha256-b66f7ebdff5bce05eaab90c6812ffc0f9ec729f6df1dc4a8bca90368406b4c21
8
8
  ---
9
9
 
10
10
  # MyDatabaseAPI
@@ -105,3 +105,5 @@ myapi database get "by-email:$EMAIL" --ns users --json | jq -r .value
105
105
  - **Free in v1.** Metered later if usage shows a need. Cost discipline still applies — store data, not blobs.
106
106
 
107
107
  Run `myapi database --help` for inline reference.
108
+
109
+ **End-to-end example:** `examples/authenticated-app/` walks the full seam — hosted login → token verification → per-user KV record → deployed container — including the parts that cost real users hours (KV writes must be wrapped as `{"value": …}`).
@@ -29,7 +29,7 @@ myapi email message send \
29
29
  ## Authentication
30
30
 
31
31
  ```bash
32
- export MYAPI_KEY=mak_...
32
+ export MYAPI_KEY=hq_live_...
33
33
  ```
34
34
 
35
35
  Requires:
@@ -21,7 +21,7 @@ myapi email verify alice@example.com --json # full check breakdown
21
21
  ## Authentication
22
22
 
23
23
  ```bash
24
- export MYAPI_KEY=mak_...
24
+ export MYAPI_KEY=hq_live_...
25
25
  ```
26
26
 
27
27
  Requires `api_key` and `org_id` from **myapihq**.
@@ -28,7 +28,7 @@ git push origin main
28
28
  ## Authentication
29
29
 
30
30
  ```bash
31
- export MYAPI_KEY=mak_...
31
+ export MYAPI_KEY=hq_live_...
32
32
  ```
33
33
 
34
34
  Requires `api_key` and `org_id` from **myapihq**. For real-git clone/push, the
@@ -20,7 +20,7 @@ myapi image generate "A clean flat-color logo for a sustainable jam company"
20
20
  ## Authentication
21
21
 
22
22
  ```bash
23
- export MYAPI_KEY=mak_...
23
+ export MYAPI_KEY=hq_live_...
24
24
  ```
25
25
 
26
26
  Requires `api_key` and `org_id` from **myapihq**. ~$0.05 per generation.
@@ -29,7 +29,7 @@ myapi llm draft --kind email --prompt "Friendly welcome, under 60 words"
29
29
  ## Authentication
30
30
 
31
31
  ```bash
32
- export MYAPI_KEY=mak_...
32
+ export MYAPI_KEY=hq_live_...
33
33
  ```
34
34
 
35
35
  Requires `api_key` and `org_id` from **myapihq**. Inference cost is debited from your MyAPI balance — top up via `myapi billing topup`, or keep it funded automatically with `myapi billing auto-recharge`.
@@ -23,7 +23,7 @@ myapi people get person_01
23
23
  ## Authentication
24
24
 
25
25
  ```bash
26
- export MYAPI_KEY=mak_...
26
+ export MYAPI_KEY=hq_live_...
27
27
  ```
28
28
 
29
29
  Requires `api_key` and `org_id` from **myapihq**.
@@ -22,7 +22,7 @@ myapi pixel interactions --since 24h
22
22
  ## Authentication
23
23
 
24
24
  ```bash
25
- export MYAPI_KEY=mak_...
25
+ export MYAPI_KEY=hq_live_...
26
26
  ```
27
27
 
28
28
  Requires `api_key` and `org_id` from **myapihq**.
@@ -22,7 +22,7 @@ myapi queue jobs thumbnails
22
22
  ## Authentication
23
23
 
24
24
  ```bash
25
- export MYAPI_KEY=mak_...
25
+ export MYAPI_KEY=hq_live_...
26
26
  ```
27
27
 
28
28
  Requires `api_key` + `org_id` from **myapihq**.
@@ -23,7 +23,7 @@ curl -O "$(myapi storage get-url <asset_id>)"
23
23
  ## Authentication
24
24
 
25
25
  ```bash
26
- export MYAPI_KEY=mak_...
26
+ export MYAPI_KEY=hq_live_...
27
27
  ```
28
28
 
29
29
  Requires `api_key` and `org_id` from **myapihq**.
@@ -24,7 +24,7 @@ myapi task claim <id> && myapi task resolve <id>
24
24
  ## Authentication
25
25
 
26
26
  ```bash
27
- export MYAPI_KEY=mak_...
27
+ export MYAPI_KEY=hq_live_...
28
28
  ```
29
29
 
30
30
  Requires `api_key` + `org_id` from **myapihq**.
@@ -28,7 +28,7 @@ myapi webhook delivery <delivery_id>
28
28
  ## Authentication
29
29
 
30
30
  ```bash
31
- export MYAPI_KEY=mak_...
31
+ export MYAPI_KEY=hq_live_...
32
32
  ```
33
33
 
34
34
  Requires `api_key` and `org_id` from **myapihq**.
@@ -4,7 +4,7 @@ version: 1.0.0
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-13680a0decdbb5b49d9d80b52203eb68a43450f94477936252b54dfd2b3845bc
7
+ checksum: sha256-82666c883230a775b07c8e1ec6243e812e58f246dd9a7cf1659aee77b877ee73
8
8
  ---
9
9
 
10
10
  # MyWebhookAPI
@@ -32,6 +32,7 @@ Treat the webhook slug as a secret — anyone with the URL can POST.
32
32
  | `myapi webhook create "<name>" [--description "..."]` | Create an endpoint, returns id + inbound URL |
33
33
  | `myapi webhook list` | List all endpoints in your org |
34
34
  | `myapi webhook delete <id>` | Delete an endpoint and all its deliveries |
35
+ | `myapi webhook deliveries [endpoint_id]` | List what came in, newest first (`--limit`, `--cursor`) |
35
36
  | `myapi webhook delivery <delivery_id>` | Get the full payload + headers for one delivery |
36
37
  <!-- generated:end -->
37
38
 
@@ -46,9 +47,12 @@ myapi webhook create "stripe-events" --description "Stripe payment events"
46
47
  curl -X POST https://api.mywebhookapi.com/webhook/in/<slug> \
47
48
  -H "Content-Type: application/json" \
48
49
  -d '{"event":"payment.succeeded"}'
49
- # → responds with {"delivery_id":"<uuid>"} — capture this to inspect later
50
+ # → responds with {"delivery_id":"<uuid>"}
50
51
 
51
- # 3. Inspect a delivery by id (there is no list-deliveries route)
52
+ # 3. See what actually arrived list, then read one in full.
53
+ # Don't rely on capturing delivery_id at POST time: for a funnel form that
54
+ # response goes to the visitor's browser, not to you.
55
+ myapi webhook deliveries
52
56
  myapi webhook delivery <delivery_id>
53
57
  ```
54
58
 
@@ -24,7 +24,7 @@ myapi workflow create "Slack on lead" \
24
24
  ## Authentication
25
25
 
26
26
  ```bash
27
- export MYAPI_KEY=mak_...
27
+ export MYAPI_KEY=hq_live_...
28
28
  ```
29
29
 
30
30
  Requires `api_key` + `org_id` from **myapihq**, and a webhook endpoint id from **mywebhookapi**.