@myapihq/cli 2.10.0 → 2.11.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 (48) hide show
  1. package/dist/commands/import-key.test.d.ts +1 -0
  2. package/dist/commands/import-key.test.js +69 -0
  3. package/dist/commands/org.js +5 -1
  4. package/dist/commands/setup.d.ts +5 -1
  5. package/dist/commands/setup.js +67 -6
  6. package/dist/commands/storage.js +151 -14
  7. package/dist/config.d.ts +2 -0
  8. package/dist/errors.d.ts +4 -0
  9. package/dist/errors.js +30 -3
  10. package/dist/helpers.d.ts +20 -1
  11. package/dist/helpers.js +79 -1
  12. package/dist/index.js +6 -1
  13. package/dist/org-not-found.test.d.ts +1 -0
  14. package/dist/org-not-found.test.js +42 -0
  15. package/dist/org-notice.test.d.ts +1 -0
  16. package/dist/org-notice.test.js +37 -0
  17. package/dist/output.d.ts +6 -0
  18. package/dist/output.js +8 -1
  19. package/dist/skills/my-api-hq/SKILL.md +34 -14
  20. package/dist/skills/my-audience-api/SKILL.md +16 -1
  21. package/dist/skills/my-auth-api/SKILL.md +17 -2
  22. package/dist/skills/my-company-api/SKILL.md +16 -1
  23. package/dist/skills/my-container-api/SKILL.md +22 -7
  24. package/dist/skills/my-crm-api/SKILL.md +16 -1
  25. package/dist/skills/my-database-api/SKILL.md +13 -17
  26. package/dist/skills/my-domain-api/SKILL.md +16 -1
  27. package/dist/skills/my-email-api/SKILL.md +12 -16
  28. package/dist/skills/my-email-verify-api/SKILL.md +16 -1
  29. package/dist/skills/my-feedback-api/README.md +33 -0
  30. package/dist/skills/my-feedback-api/SKILL.md +118 -0
  31. package/dist/skills/my-feedback-api/claude/.claude-plugin/plugin.json +7 -0
  32. package/dist/skills/my-function-api/README.md +34 -0
  33. package/dist/skills/my-function-api/SKILL.md +16 -1
  34. package/dist/skills/my-funnel-api/SKILL.md +16 -1
  35. package/dist/skills/my-git-api/SKILL.md +17 -1
  36. package/dist/skills/my-image-api/SKILL.md +17 -1
  37. package/dist/skills/my-llm-api/SKILL.md +20 -6
  38. package/dist/skills/my-payments-api/README.md +33 -0
  39. package/dist/skills/my-payments-api/SKILL.md +16 -1
  40. package/dist/skills/my-people-api/SKILL.md +16 -1
  41. package/dist/skills/my-pixel-api/SKILL.md +26 -3
  42. package/dist/skills/my-queue-api/SKILL.md +35 -2
  43. package/dist/skills/my-storage-api/SKILL.md +37 -29
  44. package/dist/skills/my-task-api/SKILL.md +35 -2
  45. package/dist/skills/my-url-to/SKILL.md +16 -1
  46. package/dist/skills/my-webhook-api/SKILL.md +23 -3
  47. package/dist/skills/my-workflow-api/SKILL.md +32 -6
  48. package/package.json +2 -2
@@ -0,0 +1,42 @@
1
+ // A resource sitting in another org answers exactly like a typo'd id. "Not
2
+ // found" sends the reader to check the id — which is correct, and which is why
3
+ // the org never gets questioned. Naming the org that was searched is what
4
+ // turns the commonest org-drift symptom into its own diagnosis.
5
+ //
6
+ // The risk on the other side is appending it to everything, which is the
7
+ // always-on banner in a different costume. These pin both edges.
8
+ import { describe, it, expect } from 'vitest';
9
+ import { withOrgContext } from './errors.js';
10
+ const ORG = { org_id: 'org-aaaaaaaa-1111', org_name: 'Acme' };
11
+ describe('withOrgContext', () => {
12
+ it('names the org searched on a 404', () => {
13
+ const out = withOrgContext('Funnel not found.', { status: 404, code: 'FUNNEL_NOT_FOUND' }, ORG);
14
+ expect(out).toContain('Funnel not found.');
15
+ expect(out).toContain('Acme');
16
+ expect(out).toContain('myapi org list');
17
+ });
18
+ it('fires on a not-found CODE even when the status is not 404', () => {
19
+ const out = withOrgContext('Resource not found.', { status: 400, code: 'not_found' }, ORG);
20
+ expect(out).toContain('Acme');
21
+ });
22
+ it('stays out of the way on unrelated errors', () => {
23
+ const msg = 'Too many requests. Please wait a moment and try again.';
24
+ expect(withOrgContext(msg, { status: 429, code: 'RATE_LIMITED' }, ORG)).toBe(msg);
25
+ });
26
+ it('says nothing when the org itself is what was not found', () => {
27
+ // "Looked in org X" where X is the id that does not exist restates the
28
+ // failure as though it were a clue.
29
+ const msg = 'Organization not found.';
30
+ expect(withOrgContext(msg, { status: 404, code: 'ORG_NOT_FOUND' }, ORG)).toBe(msg);
31
+ });
32
+ it('says nothing when no org was resolved', () => {
33
+ // Account-level commands (whoami, billing) never resolve an org; there is
34
+ // no org to blame and claiming one would be a fabrication.
35
+ const msg = 'Resource not found.';
36
+ expect(withOrgContext(msg, { status: 404, code: 'NOT_FOUND' }, undefined)).toBe(msg);
37
+ });
38
+ it('does not repeat an org the message already names', () => {
39
+ const msg = `Funnel not found in org ${ORG.org_id}.`;
40
+ expect(withOrgContext(msg, { status: 404, code: 'FUNNEL_NOT_FOUND' }, ORG)).toBe(msg);
41
+ });
42
+ });
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,37 @@
1
+ // The org-change notice has two silent failure modes and no build breaks for
2
+ // either: always-speak turns it into noise that gets filtered out within a day
3
+ // (and costs tokens on every call in an agent loop), always-silent lets the
4
+ // wrong-org write it exists to catch go through unannounced. Pin both edges.
5
+ import { describe, it, expect } from 'vitest';
6
+ import { orgChangeNotice } from './helpers.js';
7
+ const NAMES = { 'org-a': 'Acme', 'org-b': 'Demo Corp' };
8
+ describe('orgChangeNotice', () => {
9
+ it('says nothing in steady state — the same org twice', () => {
10
+ expect(orgChangeNotice('org-a', 'org-a', NAMES)).toBeUndefined();
11
+ });
12
+ it('says nothing on the first ever resolution', () => {
13
+ // No prior means nothing has moved; announcing here would fire on every
14
+ // fresh config and train the reader to ignore the line.
15
+ expect(orgChangeNotice(undefined, 'org-a', NAMES)).toBeUndefined();
16
+ });
17
+ it('speaks when the org moves, naming both sides', () => {
18
+ const msg = orgChangeNotice('org-a', 'org-b', NAMES);
19
+ expect(msg).toBeDefined();
20
+ expect(msg).toContain('Acme');
21
+ expect(msg).toContain('Demo Corp');
22
+ });
23
+ it('still speaks when no name is cached, falling back to the id', () => {
24
+ // The cache is populated opportunistically by `org list` / `status`, so an
25
+ // agent that never ran either still has to be told the target moved.
26
+ const msg = orgChangeNotice('org-a', 'org-b', {});
27
+ expect(msg).toBeDefined();
28
+ expect(msg).toContain('org-a');
29
+ expect(msg).toContain('org-b');
30
+ });
31
+ it('shortens long ids so the line stays scannable', () => {
32
+ const long = 'org-7f1aa7f1-f6eb-4a6d-87b7-1bbe6584f5d6';
33
+ const msg = orgChangeNotice(long, 'org-b', {});
34
+ expect(msg).not.toContain(long);
35
+ expect(msg).toContain('org-7f1a');
36
+ });
37
+ });
package/dist/output.d.ts CHANGED
@@ -5,6 +5,11 @@ export declare function success(message: string): void;
5
5
  export declare function error(message: string): never;
6
6
  export declare function info(message: string): void;
7
7
  export declare function banner(message: string): void;
8
+ type ResolvedContext = {
9
+ org_id: string;
10
+ org_name?: string;
11
+ };
12
+ export declare function setResolvedContextSource(fn: () => ResolvedContext | undefined): void;
8
13
  export declare function printJson(data: unknown): void;
9
14
  export declare function spinnerFrame(i: number): string;
10
15
  export declare function spinnerWrite(s: string): void;
@@ -16,3 +21,4 @@ export interface PrintTableOptions {
16
21
  empty?: string;
17
22
  }
18
23
  export declare function printTable<T extends object>(rows: T[], opts?: PrintTableOptions): void;
24
+ export {};
package/dist/output.js CHANGED
@@ -12,8 +12,15 @@ export function info(message) {
12
12
  export function banner(message) {
13
13
  process.stderr.write(message + '\n');
14
14
  }
15
+ let resolvedContext;
16
+ export function setResolvedContextSource(fn) {
17
+ resolvedContext = fn;
18
+ }
15
19
  export function printJson(data) {
16
- console.log(JSON.stringify(data, null, 2));
20
+ const ctx = resolvedContext?.();
21
+ const stampable = ctx && data !== null && typeof data === 'object' && !Array.isArray(data);
22
+ const payload = stampable ? { ...data, _resolved: ctx } : data;
23
+ console.log(JSON.stringify(payload, null, 2));
17
24
  }
18
25
  // Spinner / line-clear primitives. Used by polling helpers (utils.pollJob)
19
26
  // and any handler that wants its own progress UI.
@@ -1,10 +1,10 @@
1
1
  ---
2
2
  name: my-api-hq
3
- version: 1.0.0
3
+ version: 1.1.0
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-fab324491446026e1600ac531bfbb164c10d46a617e528e6886be74b7dbdb4b1
7
+ checksum: sha256-6f54c719facde1ddcdee04ffcf9527ed42550cbfc67679ced5aea55fc7adba7d
8
8
  ---
9
9
 
10
10
  # MyApiHQ
@@ -15,14 +15,6 @@ The root service. It manages accounts, API keys, organizations, and billing. No
15
15
  <!-- llm:start -->
16
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.
17
17
 
18
- ```
19
- myapihq ──► org_id + api_key
20
-
21
- ┌───────┴────────┐
22
- mydomainapi myfunnelapi
23
- (domains) (websites)
24
- ```
25
-
26
18
  ### Anonymous vs registered accounts
27
19
 
28
20
  Two tiers, chosen at setup time:
@@ -42,6 +34,7 @@ An anonymous account can upgrade at any time via `myapi account link <email>`
42
34
  | Command | What it does |
43
35
  |---|---|
44
36
  | `myapi account setup` | Interactive setup: creates account, generates api_key, sets defaults |
37
+ | `myapi status` | Single-screen orientation: account + every resource in the default org. Start here when you don't know what already exists |
45
38
  | `myapi account whoami` | Show current account, default org/funnel, balance, free-tier usage |
46
39
  | `myapi account link [email]` | Upgrade anonymous account to registered (or add a second session) |
47
40
  | `myapi account switch [index]` | Switch active account |
@@ -72,6 +65,7 @@ myapi account setup
72
65
  myapi org create "Acme" --yes
73
66
 
74
67
  # Day-to-day
68
+ myapi status # what exists already, in one screen
75
69
  myapi account whoami # confirm what's active
76
70
  myapi billing balance # before doing anything that costs credits
77
71
  myapi billing topup 20 # add $20
@@ -98,28 +92,54 @@ Each org gets a free preview subdomain (`*.makeautonomous.com`) usable before re
98
92
  - API keys have format `hq_live_...` and are sent as `Authorization: Bearer <key>`.
99
93
  - `org sync-brand` is async (scrapes the site, polls the job).
100
94
 
101
- ## Minting a least-privilege API key
95
+ ## Scoping a key and why `--org` is the one that saves you
102
96
 
103
97
  A key's authority is inline and always a subset of the key that mints it, so
104
98
  you can hand work a key that cannot exceed its job:
105
99
 
106
100
  ```bash
101
+ myapi keys create --name agent --org <id> --grant '*' # locked to ONE org
107
102
  myapi keys create --name ci --grant funnel:write,storage:read
108
103
  myapi keys create --name readonly --grant '*:read' # read anything, write nothing
109
104
  myapi keys create --name billing-fn --org <id> --grant email --spend-cap 25
110
105
  ```
111
106
 
107
+ - `--org <id>` — **lock it to one org.** Omit for account-wide.
112
108
  - `--grant <list>` — `slot:read` / `slot:write`; a bare slot means write, `*`
113
109
  means all. **Omitting `--grant` mints an unrestricted key.**
114
- - `--org <id>` — lock it to one org. Omit for account-wide.
115
110
  - `--spend-cap <usd>` — hard ceiling; `0` means the key cannot spend at all.
116
111
  - `keys revoke-all --kind function|manual|account` narrows the kill switch.
117
112
 
113
+ **Agents working for one org: use an org-locked key.** The org is otherwise
114
+ ambient — a saved default that survives across turns, where a stale one looks
115
+ exactly like a correct one. That is how a demo lands on a live site. Locking
116
+ turns a wrong-org write into a `403`. Keep your *own* key account-wide though:
117
+ an org-locked one cannot create orgs or manage billing.
118
+
119
+ Two more signals, no flags needed: resolving to a different org than last time
120
+ prints `→ org changed: <from> → <to>` on stderr, once — silence means it has
121
+ not moved. Under `--json`, object responses carry `_resolved: {org_id,
122
+ org_name}`; assert on that rather than assuming.
123
+
118
124
  ## Org profile fields
119
125
 
120
126
  `myapi org create <name>` also takes `--tagline`, `--description`,
121
127
  `--business-sector` and `--logo-url`. They populate the org's public profile
122
- and the funnel created alongside it, so setting them at create avoids editing
123
- two places later.
128
+ and the funnel created alongside it.
129
+
130
+ ## HTTP (from deployed code)
131
+
132
+ <!-- http:start -->
133
+ <!-- generated by `npm run canonical-sync` — do not edit -->
134
+ ```
135
+ base https://api.myapihq.com
136
+ path GET /hq/orgs/{org_id}
137
+ auth Authorization: Bearer <key> (fn: env.__MYAPI_KEY · container: env.MYAPI_KEY)
138
+ reply { "success": true, "data": …, "error": null, "meta": {…} }
139
+ ```
140
+
141
+ - **Per-slot host** — do not assume one host serves every slot.
142
+ - **Org id goes in the PATH** — there is no `X-Org-Id` header.
143
+ <!-- http:end -->
124
144
 
125
145
  Run `myapi --help` or `myapi <command> --help` for full flag reference.
@@ -4,7 +4,7 @@ version: 1.0.0
4
4
  description: >
5
5
  Saved audiences = named Goldfox-filter snapshots over the people or company database. Build a target list once, name it, reuse it across campaigns, refresh to re-evaluate against current data. The persistence layer on top of my-people-api + my-company-api.
6
6
  triggers: [audience, segment, target list, saved filter, goldfox, lead list, abm list, refresh, members, prospect database]
7
- checksum: sha256-a2c7142e880932ac2aa53cdb29b8632ce2c4d5ef33c3f8b6df705f7e987ab6af
7
+ checksum: sha256-3fc9f2966804381cb9e2b5f52d44a0c12c857f243b151a93a813c6345574f07f
8
8
  ---
9
9
 
10
10
  # MyAudienceAPI
@@ -136,4 +136,19 @@ myapi audience create "EU growth-stage SaaS accounts" \
136
136
  - Audiences are per-org. Two orgs can have audiences with the same name; ids are globally unique.
137
137
  - Look before you delete: confirm the target with `myapi audience get <id>`, pass `--org` explicitly; delete verbs require `--yes` in non-interactive runs.
138
138
 
139
+ ## HTTP (from deployed code)
140
+
141
+ <!-- http:start -->
142
+ <!-- generated by `npm run canonical-sync` — do not edit -->
143
+ ```
144
+ base https://api.myapihq.com
145
+ path POST /audience/orgs/{org_id}/audiences
146
+ auth Authorization: Bearer <key> (fn: env.__MYAPI_KEY · container: env.MYAPI_KEY)
147
+ reply { "success": true, "data": …, "error": null, "meta": {…} }
148
+ ```
149
+
150
+ - **Per-slot host** — do not assume one host serves every slot.
151
+ - **Org id goes in the PATH** — there is no `X-Org-Id` header.
152
+ <!-- http:end -->
153
+
139
154
  Run `myapi audience --help` for full flag reference.
@@ -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-5eeacc463d7323f85a330639b7073f735ccc80bddfb8217c9c15c56989b8a99a
7
+ checksum: sha256-07690e940717e8f65c195f9fbecdc1def9372edfd52dea19f6570227008d28ad
8
8
  ---
9
9
 
10
10
  # MyAuthAPI
@@ -121,4 +121,19 @@ with no email — for throwaway or machine-owned orgs. It cannot receive
121
121
  password resets or magic links, so attach a real identity before anything
122
122
  depends on it. `myapi status` shows `Type: anonymous`.
123
123
 
124
- **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`).
124
+ **End-to-end example:** `examples/authenticated-app/` in github.com/myapihq/myapi 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`).
125
+
126
+ ## HTTP (from deployed code)
127
+
128
+ <!-- http:start -->
129
+ <!-- generated by `npm run canonical-sync` — do not edit -->
130
+ ```
131
+ base https://api.myapihq.com
132
+ path POST /auth/orgs/{org_id}/tenant
133
+ auth Authorization: Bearer <key> (fn: env.__MYAPI_KEY · container: env.MYAPI_KEY)
134
+ reply { "success": true, "data": …, "error": null, "meta": {…} }
135
+ ```
136
+
137
+ - **Per-slot host** — do not assume one host serves every slot.
138
+ - **Org id goes in the PATH** — there is no `X-Org-Id` header.
139
+ <!-- http:end -->
@@ -4,7 +4,7 @@ version: 1.0.0
4
4
  description: >
5
5
  Company database backed by the Goldfox crawl. Filter companies by Goldfox confidence tier, country/TLD consistency, behavioral page signals (has_careers_page, has_investors_page, has_shop_page, has_c_level, has_decision_maker), legal-entity status, headcount, and source-URL count. Account-based targeting and B2B firmographics.
6
6
  triggers: [companies, accounts, firmographics, abm, search, filter, goldfox, careers signal, investors, c-level, shop, b2b targeting]
7
- checksum: sha256-4754de8a66bf13e7e2f2a23acc407ccec147e142abf8adc670ae7962ca860a74
7
+ checksum: sha256-5204c6218af2d4c52b1b81aa0d917b69c863be1d3c0384d2d2f7d9dda1587534
8
8
  ---
9
9
 
10
10
  # MyCompanyAPI
@@ -107,4 +107,19 @@ myapi audience members $AID --limit 50 --json > accounts.json
107
107
  - `keyword` is a substring match on the company's **domain** — use `--keyword stripe` to find domains containing "stripe".
108
108
  - For a persistent account list, use `my-audience-api` with `--from company`.
109
109
 
110
+ ## HTTP (from deployed code)
111
+
112
+ <!-- http:start -->
113
+ <!-- generated by `npm run canonical-sync` — do not edit -->
114
+ ```
115
+ base https://api.myapihq.com
116
+ path POST /company/orgs/{org_id}/search
117
+ auth Authorization: Bearer <key> (fn: env.__MYAPI_KEY · container: env.MYAPI_KEY)
118
+ reply { "success": true, "data": …, "error": null, "meta": {…} }
119
+ ```
120
+
121
+ - **Per-slot host** — do not assume one host serves every slot.
122
+ - **Org id goes in the PATH** — there is no `X-Org-Id` header.
123
+ <!-- http:end -->
124
+
110
125
  Run `myapi company --help` for full flag reference.
@@ -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-0e68a4596027c9ccccf0cc86423deffbe71307d265aa973d3cd6261db2695152
7
+ checksum: sha256-a5aabfe95b85f1e6ceb2b9254441183d540e81f888cefbdcf302385b95db2fd0
8
8
  ---
9
9
 
10
10
  # MyContainerAPI
@@ -92,14 +92,14 @@ myapi container deploy <id> --source ./my-app
92
92
  # 2b. Or ship an image you already built and pushed.
93
93
  myapi container deploy <id> registry.example.com/my-app:v1
94
94
 
95
- # Either way this takes 100% of traffic immediately there is no staging
96
- # step, so verify on a non-production container FIRST.
95
+ # A plain deploy takes 100% of traffic the moment it lands. Gate it with
96
+ # --smoke, or hold it back with --no-promote and test the revision yourself.
97
97
 
98
- # 3. Check what you actually shipped. Assert on content: a build whose
99
- # frontend never bundled still binds its port and returns 200.
98
+ # 3. Verify content, not status: a build whose frontend never bundled
99
+ # still binds its port and returns 200.
100
100
  curl -s https://<your-domain>/ | grep -q 'assets/' || echo "BROKEN BUILD"
101
101
 
102
- # 3. Serve it on a custom domain. The parent domain must already be
102
+ # 4. Serve it on a custom domain. The parent domain must already be
103
103
  # registered: myapi domain register synthesisdaily.com
104
104
  myapi container domain <id> app.synthesisdaily.com
105
105
  # → app.synthesisdaily.com now serves the container over HTTPS
@@ -150,6 +150,21 @@ service costs nothing while idle.
150
150
  `invalid_json_response`. Build the tarball yourself and pass
151
151
  `--source ctx.tar.gz`, or keep the directory clean.
152
152
 
153
+ ## HTTP (from deployed code)
154
+
155
+ <!-- http:start -->
156
+ <!-- generated by `npm run canonical-sync` — do not edit -->
157
+ ```
158
+ base https://api.myapihq.com
159
+ path POST /container/orgs/{org_id}/containers
160
+ auth Authorization: Bearer <key> (fn: env.__MYAPI_KEY · container: env.MYAPI_KEY)
161
+ reply { "success": true, "data": …, "error": null, "meta": {…} }
162
+ ```
163
+
164
+ - **Per-slot host** — do not assume one host serves every slot.
165
+ - **Org id goes in the PATH** — there is no `X-Org-Id` header.
166
+ <!-- http:end -->
167
+
153
168
  Run `myapi container --help` for the full flag reference.
154
169
 
155
- **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`).
170
+ **End-to-end example:** `examples/authenticated-app/` in github.com/myapihq/myapi walks the full seam — hosted login → token verification → per-user KV record → deployed container. Every constraint it hits is in **Runtime constraints** above.
@@ -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-dfadb20670d9d978503549d330c2c9717fbe6d13ac35b53d9e1a8874d2136fde
7
+ checksum: sha256-3f840c8d7205d6a3eacc7c2be3ba2580a52a1964c085af241fd9ad67d48e19b6
8
8
  ---
9
9
 
10
10
  # MyCRMAPI
@@ -167,4 +167,19 @@ myapi crm contacts events <id> --kind webhook_received
167
167
  - **Only `webhook_received` fires today.** Email and pixel auto-ingest are coming; the CLI surface will not change.
168
168
  - **Free in v1.** Metered later if usage warrants.
169
169
 
170
+ ## HTTP (from deployed code)
171
+
172
+ <!-- http:start -->
173
+ <!-- generated by `npm run canonical-sync` — do not edit -->
174
+ ```
175
+ base https://api.myapihq.com
176
+ path POST /crm/orgs/{org_id}/contacts
177
+ auth Authorization: Bearer <key> (fn: env.__MYAPI_KEY · container: env.MYAPI_KEY)
178
+ reply { "success": true, "data": …, "error": null, "meta": {…} }
179
+ ```
180
+
181
+ - **Per-slot host** — do not assume one host serves every slot.
182
+ - **Org id goes in the PATH** — there is no `X-Org-Id` header.
183
+ <!-- http:end -->
184
+
170
185
  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-924071245c33bf3e43f085fbd756af003e19c990f4522f658de33f42504452d7
7
+ checksum: sha256-9b6f14fa5b0115ce042c71f861cb71ec8993aa68447d740b63f5b3689c23b629
8
8
  ---
9
9
 
10
10
  # MyDatabaseAPI
@@ -105,26 +105,22 @@ 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
 
108
- ## Calling this from deployed code (HTTP)
109
-
110
- The CLI is not what runs in production — a deployed function or container calls
111
- the HTTP API directly. That surface was previously only discoverable by
112
- grepping the CLI bundle, which cost one team an hour per slot.
108
+ ## HTTP (from deployed code)
113
109
 
110
+ <!-- http:start -->
111
+ <!-- generated by `npm run canonical-sync` — do not edit -->
114
112
  ```
115
- base https://api.myapihq.com
116
- path /database/orgs/{org_id}/namespaces/{ns}/keys/{key}
117
- auth Authorization: Bearer <api key>
118
- (inside a function: env.__MYAPI_KEY · inside a container: env.MYAPI_KEY)
119
- body writes take {"value": <json>} the value is WRAPPED
120
- reply { "success": true, "data": …, "error": null, "meta": {…} }
121
- Unwrap `data`. On failure `success` is false and `error` is
122
- { code, message }.
113
+ base https://api.myapihq.com
114
+ path PUT /database/orgs/{org_id}/namespaces/{ns}/keys/{key}
115
+ body writes are WRAPPED: {"value": <json>} — not the bare value
116
+ auth Authorization: Bearer <key> (fn: env.__MYAPI_KEY · container: env.MYAPI_KEY)
117
+ reply { "success": true, "data": …, "error": null, "meta": {…} }
123
118
  ```
124
119
 
125
- **The org id goes in the PATH, not a header.** There is no `X-Org-Id`.
126
- **Base URLs differ per slot** — do not assume one host for everything.
120
+ - **Per-slot host** do not assume one host serves every slot.
121
+ - **Org id goes in the PATH** — there is no `X-Org-Id` header.
122
+ <!-- http:end -->
127
123
 
128
124
  Run `myapi database --help` for inline reference.
129
125
 
130
- **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": …}`).
126
+ **End-to-end example:** `examples/authenticated-app/` in github.com/myapihq/myapi 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": …}`).
@@ -4,7 +4,7 @@ version: 1.0.0
4
4
  description: >
5
5
  Register new domains and manage edge settings. Required before a funnel can go live on a custom URL.
6
6
  triggers: [domain, register domain, dns, custom domain, edge, cdn, security level, browser check, renew, namecheap]
7
- checksum: sha256-6a686b453528c69dcc791db409c04a514422951abf333c8feed7c614311815cb
7
+ checksum: sha256-ffe00d434534f444320a3e84323e87168c0c5797445fa0f39eeb55ad8278f721
8
8
  ---
9
9
 
10
10
  # MyDomainAPI
@@ -133,4 +133,19 @@ myapi domain records create <domain> --type CNAME --name app --content x.com --p
133
133
  reassign path, so pass `--org` explicitly and check `domain list --filter all`
134
134
  first.
135
135
 
136
+ ## HTTP (from deployed code)
137
+
138
+ <!-- http:start -->
139
+ <!-- generated by `npm run canonical-sync` — do not edit -->
140
+ ```
141
+ base https://api.mydomainapi.com
142
+ path GET /domain/orgs/{org_id}/list
143
+ auth Authorization: Bearer <key> (fn: env.__MYAPI_KEY · container: env.MYAPI_KEY)
144
+ reply { "success": true, "data": …, "error": null, "meta": {…} }
145
+ ```
146
+
147
+ - **Per-slot host** — do not assume one host serves every slot.
148
+ - **Org id goes in the PATH** — there is no `X-Org-Id` header.
149
+ <!-- http:end -->
150
+
136
151
  Run `myapi domain --help` or `myapi domain <subcommand> --help` for full flag reference.
@@ -4,7 +4,7 @@ version: 1.0.0
4
4
  description: >
5
5
  Send transactional and bulk email from your own domain. Create mailboxes, send/receive messages, generate AI templates, and manage warmup.
6
6
  triggers: [email, mailbox, send email, transactional email, template, warmup, inbox, outbox, ses, sender reputation]
7
- checksum: sha256-aa22381613affcff041a8d51d2acb2ba657fecefc97f4feba45958bcc11b2e06
7
+ checksum: sha256-98aedd0274a450fdd68a2d0cfe3a8af33520c58b2ce6e7cfaee7840fd5526aa6
8
8
  ---
9
9
 
10
10
  # MyEmailAPI
@@ -64,6 +64,7 @@ myapi email warmup stats --address hello@yourdomain.com
64
64
 
65
65
  - A mailbox is uniquely identified by its address (`username@domain`).
66
66
  - Sending is opt-in per mailbox. Newly-created mailboxes can receive but not send until `activate-sending` runs.
67
+ - **`402`** — `INSUFFICIENT_FUNDS`: top up or enable `myapi billing auto-recharge`. `SPEND_CAP_EXCEEDED`: raise your own ceiling with `myapi billing spend-cap`.
67
68
  - Templates are org-scoped. Set a default org once: `myapi config set-org <id>`.
68
69
 
69
70
 
@@ -81,24 +82,19 @@ Two things that read as contradictory and are not:
81
82
  to run Google Workspace or another provider on the apex, check the existing
82
83
  records first with `myapi domain records <domain>`.
83
84
 
84
- ## Calling this from deployed code (HTTP)
85
-
86
- The CLI is not what runs in production — a deployed function or container calls
87
- the HTTP API directly. That surface was previously only discoverable by
88
- grepping the CLI bundle, which cost one team an hour per slot.
85
+ ## HTTP (from deployed code)
89
86
 
87
+ <!-- http:start -->
88
+ <!-- generated by `npm run canonical-sync` — do not edit -->
90
89
  ```
91
- base https://api.myemailapi.com ← not the gateway
92
- path /email/mailboxes · /email/orgs/{org_id}/messages/send
93
- auth Authorization: Bearer <api key>
94
- (inside a function: env.__MYAPI_KEY · inside a container: env.MYAPI_KEY)
95
- body application/json
96
- reply { "success": true, "data": …, "error": null, "meta": {…} }
97
- Unwrap `data`. On failure `success` is false and `error` is
98
- { code, message }.
90
+ base https://api.myemailapi.com
91
+ path POST /email/send
92
+ auth Authorization: Bearer <key> (fn: env.__MYAPI_KEY · container: env.MYAPI_KEY)
93
+ reply { "success": true, "data": …, "error": null, "meta": {…} }
99
94
  ```
100
95
 
101
- **The org id goes in the PATH, not a header.** There is no `X-Org-Id`.
102
- **Base URLs differ per slot** — do not assume one host for everything.
96
+ - **Per-slot host** do not assume one host serves every slot.
97
+ - **Account-scoped, not org-scoped** — no `{org_id}` segment; the key identifies the account.
98
+ <!-- http:end -->
103
99
 
104
100
  Run `myapi email --help` or `myapi email <namespace> --help` for full flag reference.
@@ -4,7 +4,7 @@ version: 1.0.0
4
4
  description: >
5
5
  Synchronous single-address email verification — syntax + DNS + Microsoft GetCredentialType probe. Returns a verdict in <1s for ~50% of inputs; the rest get verdict='unknown' with smtp_recommended=true. The pre-send quality gate for any outbound campaign.
6
6
  triggers: [email verify, email validation, deliverability, smtp, syntax check, dns mx, microsoft, mx lookup, bounce prevention]
7
- checksum: sha256-a8f4d3fa04436c997d02d1d33f262190166e59e3e890b1c40a877bbd49ad3f00
7
+ checksum: sha256-c8ae836c355066eee8a12753f818d3a90c35b99983d74913c8fc76edcc99668f
8
8
  ---
9
9
 
10
10
  # MyEmailVerifyAPI
@@ -90,4 +90,19 @@ done < emails.txt | grep -v ' undeliverable$' > verified.txt
90
90
  - For bulk verification use the async batch endpoint: `myapi email verify bulk < emails.txt` (one address per line) returns a `job_id`, then poll `myapi email verify job <job_id>` for status + per-address results.
91
91
  - Verification is per-org; you'll get rate-limited if you blast more than ~1 req/sec per key.
92
92
 
93
+ ## HTTP (from deployed code)
94
+
95
+ <!-- http:start -->
96
+ <!-- generated by `npm run canonical-sync` — do not edit -->
97
+ ```
98
+ base https://api.myemailapi.com
99
+ path POST /email/orgs/{org_id}/verify
100
+ auth Authorization: Bearer <key> (fn: env.__MYAPI_KEY · container: env.MYAPI_KEY)
101
+ reply { "success": true, "data": …, "error": null, "meta": {…} }
102
+ ```
103
+
104
+ - **Per-slot host** — do not assume one host serves every slot.
105
+ - **Org id goes in the PATH** — there is no `X-Org-Id` header.
106
+ <!-- http:end -->
107
+
93
108
  Run `myapi email verify --help` for inline reference.
@@ -0,0 +1,33 @@
1
+ ---
2
+ # my-feedback-api
3
+
4
+ The loop back from the people using what you built. A page collects feedback with a public widget key that authenticates nobody; you read, filter, and close it.
5
+
6
+ ## What it does
7
+
8
+ - Public widget keys — embed in page source, restrict by origin, revoke without losing collected feedback
9
+ - Three kinds (`bug` | `issue` | `suggestion`), chosen by the reporter rather than inferred
10
+ - Filterable listing by kind and status, newest first, paged with `--limit` / `--offset`
11
+ - `resolve` closes an item; unknown ids answer identically so ids can't be probed across orgs
12
+
13
+ ## Quickstart
14
+
15
+ ```bash
16
+ myapi feedback widget create marketing --origins example.com,www.example.com
17
+ myapi feedback list --status open
18
+ myapi feedback resolve <id>
19
+ ```
20
+
21
+ ## Authentication
22
+
23
+ ```bash
24
+ export MYAPI_KEY=hq_live_...
25
+ ```
26
+
27
+ Requires `api_key` + `org_id` from **myapihq**. The widget key is separate and deliberately public — it goes in your page source.
28
+
29
+ ## Documentation
30
+
31
+ Full command reference and why `kind` is a claim rather than a guess: see `SKILL.md`.
32
+
33
+ Run `myapi feedback --help` for inline reference.