@myapihq/sdk 1.3.13 → 2.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (54) hide show
  1. package/dist/auth.d.ts +58 -0
  2. package/dist/auth.js +76 -0
  3. package/dist/client.d.ts +6 -0
  4. package/dist/client.js +16 -3
  5. package/dist/config.d.ts +1 -0
  6. package/dist/config.js +2 -1
  7. package/dist/container.d.ts +8 -0
  8. package/dist/container.js +26 -2
  9. package/dist/crm.d.ts +1 -1
  10. package/dist/crm.js +4 -1
  11. package/dist/database.js +4 -1
  12. package/dist/domain.d.ts +1 -1
  13. package/dist/email.d.ts +0 -74
  14. package/dist/email.js +1 -70
  15. package/dist/function.d.ts +4 -0
  16. package/dist/function.js +8 -0
  17. package/dist/funds.d.ts +11 -0
  18. package/dist/funds.js +55 -0
  19. package/dist/funnel.d.ts +1 -1
  20. package/dist/hq.d.ts +22 -0
  21. package/dist/hq.js +18 -0
  22. package/dist/index.d.ts +2 -0
  23. package/dist/index.js +3 -1
  24. package/dist/payments.d.ts +1 -0
  25. package/dist/payments.js +1 -1
  26. package/dist/queue.d.ts +1 -0
  27. package/dist/queue.js +5 -0
  28. package/dist/services.js +17 -8
  29. package/dist/storage.d.ts +1 -1
  30. package/dist/types.d.ts +3 -0
  31. package/dist/webhook.d.ts +4 -0
  32. package/dist/workflow.d.ts +11 -1
  33. package/dist/workflow.js +12 -2
  34. package/package.json +1 -1
  35. package/src/auth.ts +129 -0
  36. package/src/client.ts +36 -4
  37. package/src/config.ts +1 -0
  38. package/src/container.ts +53 -3
  39. package/src/crm.ts +7 -3
  40. package/src/database.ts +5 -2
  41. package/src/domain.ts +1 -1
  42. package/src/email.ts +1 -78
  43. package/src/function.ts +12 -0
  44. package/src/funds.ts +66 -0
  45. package/src/funnel.ts +1 -1
  46. package/src/hq.ts +59 -0
  47. package/src/index.ts +2 -0
  48. package/src/payments.ts +2 -1
  49. package/src/queue.ts +5 -0
  50. package/src/services.ts +17 -8
  51. package/src/storage.ts +2 -0
  52. package/src/types.ts +3 -1
  53. package/src/webhook.ts +10 -0
  54. package/src/workflow.ts +34 -3
package/dist/hq.d.ts CHANGED
@@ -138,6 +138,26 @@ export declare function setupPayment(apiKey: string): Promise<{
138
138
  export declare function topUp(apiKey: string, amountDollars: number): Promise<{
139
139
  new_balance_display: string;
140
140
  }>;
141
+ export type RechargeStatus = 'succeeded' | 'failed' | 'capped' | 'no_pm' | 'pending';
142
+ export interface AutoRechargeConfig {
143
+ enabled: boolean;
144
+ threshold_cents: number | null;
145
+ amount_cents: number | null;
146
+ monthly_cap_cents: number | null;
147
+ month_to_date_recharged_cents: number;
148
+ has_payment_method: boolean;
149
+ last_recharge_status: RechargeStatus | null;
150
+ last_recharge_attempt_at: string | null;
151
+ }
152
+ export declare function getAutoRecharge(apiKey: string): Promise<AutoRechargeConfig>;
153
+ export interface SetAutoRechargeInput {
154
+ enabled: boolean;
155
+ threshold_cents?: number;
156
+ amount_cents?: number;
157
+ monthly_cap_cents?: number;
158
+ }
159
+ export declare function setAutoRecharge(apiKey: string, input: SetAutoRechargeInput): Promise<AutoRechargeConfig>;
160
+ export declare function disableAutoRecharge(apiKey: string): Promise<void>;
141
161
  export type DoctorSeverity = 'ok' | 'warn' | 'crit';
142
162
  export interface DoctorEntityRef {
143
163
  slot: string;
@@ -152,11 +172,13 @@ export interface DoctorIssue {
152
172
  category?: string;
153
173
  message: string;
154
174
  hint?: string;
175
+ operator_only?: boolean;
155
176
  }
156
177
  export interface DoctorSection {
157
178
  name: string;
158
179
  summary: string;
159
180
  issues: DoctorIssue[];
181
+ resource_count?: number;
160
182
  }
161
183
  export interface DoctorReport {
162
184
  org_id: string;
package/dist/hq.js CHANGED
@@ -27,6 +27,9 @@ exports.getBillingHistory = getBillingHistory;
27
27
  exports.getBillingUsage = getBillingUsage;
28
28
  exports.setupPayment = setupPayment;
29
29
  exports.topUp = topUp;
30
+ exports.getAutoRecharge = getAutoRecharge;
31
+ exports.setAutoRecharge = setAutoRecharge;
32
+ exports.disableAutoRecharge = disableAutoRecharge;
30
33
  exports.getDoctor = getDoctor;
31
34
  const client_1 = require("./client");
32
35
  const config_1 = require("./config");
@@ -170,6 +173,21 @@ async function setupPayment(apiKey) {
170
173
  async function topUp(apiKey, amountDollars) {
171
174
  return (0, client_1.request)('POST', `${config_1.HQ_BASE}/hq/billing/topup`, apiKey, { amount_dollars: amountDollars });
172
175
  }
176
+ async function getAutoRecharge(apiKey) {
177
+ return (0, client_1.request)('GET', `${config_1.HQ_BASE}/hq/billing/auto-recharge`, apiKey);
178
+ }
179
+ // Enable/update auto-recharge. The backend validates invariants and rejects
180
+ // with a 400 (`MISSING_FIELDS`, `AMOUNT_BELOW_FLOOR` [$5 floor],
181
+ // `AMOUNT_LT_THRESHOLD`, `CAP_LT_AMOUNT`, `NO_PAYMENT_METHOD`) — surfaced as a
182
+ // MyApiError. Returns the full config on success.
183
+ async function setAutoRecharge(apiKey, input) {
184
+ return (0, client_1.request)('PUT', `${config_1.HQ_BASE}/hq/billing/auto-recharge`, apiKey, input);
185
+ }
186
+ // Disable auto-recharge (the threshold/amount/cap are preserved for easy
187
+ // re-enable). Backend returns 204 No Content.
188
+ async function disableAutoRecharge(apiKey) {
189
+ return (0, client_1.request)('DELETE', `${config_1.HQ_BASE}/hq/billing/auto-recharge`, apiKey);
190
+ }
173
191
  async function getDoctor(apiKey, orgId) {
174
192
  return (0, client_1.request)('GET', `${config_1.HQ_BASE}/hq/orgs/${encodeURIComponent(orgId)}/doctor`, apiKey);
175
193
  }
package/dist/index.d.ts CHANGED
@@ -1,6 +1,8 @@
1
1
  export * from './types';
2
2
  export * from './client';
3
+ export * from './funds';
3
4
  export * as hq from './hq';
5
+ export * as auth from './auth';
4
6
  export * as domain from './domain';
5
7
  export * as email from './email';
6
8
  export * as funnel from './funnel';
package/dist/index.js CHANGED
@@ -36,9 +36,10 @@ var __importStar = (this && this.__importStar) || (function () {
36
36
  };
37
37
  })();
38
38
  Object.defineProperty(exports, "__esModule", { value: true });
39
- exports.task = exports.queue = exports.git = exports.container = exports.payments = exports.fn = exports.crm = exports.database = exports.llm = exports.audience = exports.company = exports.people = exports.url = exports.workflow = exports.webhook = exports.storage = exports.pixel = exports.image = exports.funnel = exports.email = exports.domain = exports.hq = void 0;
39
+ exports.task = exports.queue = exports.git = exports.container = exports.payments = exports.fn = exports.crm = exports.database = exports.llm = exports.audience = exports.company = exports.people = exports.url = exports.workflow = exports.webhook = exports.storage = exports.pixel = exports.image = exports.funnel = exports.email = exports.domain = exports.auth = exports.hq = void 0;
40
40
  __exportStar(require("./types"), exports);
41
41
  __exportStar(require("./client"), exports);
42
+ __exportStar(require("./funds"), exports);
42
43
  // Note: config constants (STORAGE_BASE etc.) are NOT re-exported from the
43
44
  // barrel. TypeScript compiles `export * from './config'` to a runtime
44
45
  // `__exportStar` call that Node's cjs-module-lexer can't see through, so
@@ -46,6 +47,7 @@ __exportStar(require("./client"), exports);
46
47
  // CJS-compiled SDK. If you need a base URL in a downstream package, mirror
47
48
  // the constant locally — it's one line and the comment makes it obvious.
48
49
  exports.hq = __importStar(require("./hq"));
50
+ exports.auth = __importStar(require("./auth"));
49
51
  exports.domain = __importStar(require("./domain"));
50
52
  exports.email = __importStar(require("./email"));
51
53
  exports.funnel = __importStar(require("./funnel"));
@@ -1,6 +1,7 @@
1
1
  import type { Exposes } from './exposes';
2
2
  export declare const EXPOSES: Exposes;
3
3
  export interface ConnectStatus {
4
+ connected?: boolean;
4
5
  tier: string;
5
6
  stripe_account_id: string;
6
7
  onboarding_status: string;
package/dist/payments.js CHANGED
@@ -32,7 +32,7 @@ async function connect(apiKey, orgId, stripeSecretKey, tier = 't0') {
32
32
  stripe_secret_key: stripeSecretKey,
33
33
  });
34
34
  }
35
- // getConnect reads the org's connection status. Throws NOT_CONNECTED (404)
35
+ // getConnect reads the org's connection status. Returns { connected: false } when no Stripe is linked (no longer a 404).
36
36
  // when the org has not connected Stripe.
37
37
  async function getConnect(apiKey, orgId) {
38
38
  return (0, client_1.request)('GET', `${config_1.PAYMENTS_BASE}/payments/orgs/${encodeURIComponent(orgId)}/connect`, apiKey);
package/dist/queue.d.ts CHANGED
@@ -41,6 +41,7 @@ export interface EnqueueOptions {
41
41
  export declare function createQueue(apiKey: string, orgId: string, opts: CreateQueueOptions): Promise<Queue>;
42
42
  export declare function listQueues(apiKey: string, orgId: string): Promise<Queue[]>;
43
43
  export declare function getQueue(apiKey: string, orgId: string, name: string): Promise<Queue>;
44
+ export declare function deleteQueue(apiKey: string, orgId: string, name: string): Promise<void>;
44
45
  export declare function enqueueJob(apiKey: string, orgId: string, name: string, opts?: EnqueueOptions): Promise<Job>;
45
46
  export declare function listJobs(apiKey: string, orgId: string, name: string, opts?: {
46
47
  status?: string;
package/dist/queue.js CHANGED
@@ -4,6 +4,7 @@ exports.EXPOSES = void 0;
4
4
  exports.createQueue = createQueue;
5
5
  exports.listQueues = listQueues;
6
6
  exports.getQueue = getQueue;
7
+ exports.deleteQueue = deleteQueue;
7
8
  exports.enqueueJob = enqueueJob;
8
9
  exports.listJobs = listJobs;
9
10
  exports.getJob = getJob;
@@ -17,6 +18,7 @@ exports.EXPOSES = [
17
18
  'POST /queue/orgs/{org_id}/queues',
18
19
  'GET /queue/orgs/{org_id}/queues',
19
20
  'GET /queue/orgs/{org_id}/queues/{name}',
21
+ 'DELETE /queue/orgs/{org_id}/queues/{name}',
20
22
  'POST /queue/orgs/{org_id}/queues/{name}/jobs',
21
23
  'GET /queue/orgs/{org_id}/queues/{name}/jobs',
22
24
  'GET /queue/orgs/{org_id}/jobs/{id}',
@@ -41,6 +43,9 @@ async function listQueues(apiKey, orgId) {
41
43
  async function getQueue(apiKey, orgId, name) {
42
44
  return (0, client_1.request)('GET', `${queuesBase(orgId)}/${encodeURIComponent(name)}`, apiKey);
43
45
  }
46
+ async function deleteQueue(apiKey, orgId, name) {
47
+ return (0, client_1.request)('DELETE', `${queuesBase(orgId)}/${encodeURIComponent(name)}`, apiKey);
48
+ }
44
49
  // enqueueJob is idempotent on (queue, dedup_key). A job with unsucceeded
45
50
  // depends_on starts blocked.
46
51
  async function enqueueJob(apiKey, orgId, name, opts = {}) {
package/dist/services.js CHANGED
@@ -28,6 +28,15 @@ exports.SERVICES = [
28
28
  status: 'ga',
29
29
  keywords: k('auth', 'identity', 'billing', 'organization'),
30
30
  },
31
+ {
32
+ module: 'auth',
33
+ skill: 'my-auth-api',
34
+ domain: 'myauthapi.com',
35
+ description: 'Managed OIDC identity provider for the end users of apps built on MyAPI. Per-org auth tenant + OIDC clients, RS256/JWKS, hosted login, managed Google sign-in. A Kinde alternative.',
36
+ category: 'identity',
37
+ status: 'ga',
38
+ keywords: k('auth', 'oidc', 'oauth', 'login', 'identity', 'sso'),
39
+ },
31
40
  {
32
41
  module: 'domain',
33
42
  skill: 'my-domain-api',
@@ -39,23 +48,23 @@ exports.SERVICES = [
39
48
  },
40
49
  // ── send ────────────────────────────────────────────────────────────
41
50
  {
42
- // Customer-facing email surface — mailboxes, campaigns, templates,
43
- // warmup. Launched 2026-05-17: the backend lifted the pre-launch gate
51
+ // Customer-facing email surface — mailboxes, transactional send,
52
+ // templates, warmup. Launched 2026-05-17: the backend lifted the pre-launch gate
44
53
  // (the [disabled, pre-launch] / 503 SERVICE_NOT_LAUNCHED state is gone).
45
54
  // Sibling `my-email-verify-api` is the sync single-address verifier.
46
55
  module: 'email',
47
56
  skill: 'my-email-api',
48
57
  domain: 'myemailapi.com',
49
- description: 'Send transactional and bulk email from your own domain. Mailboxes, AI templates, drip campaigns, and warmup.',
58
+ description: 'Send transactional and bulk email from your own domain. Mailboxes, AI templates, and warmup.',
50
59
  category: 'send',
51
60
  status: 'ga',
52
- keywords: k('email', 'transactional', 'campaign', 'smtp'),
61
+ keywords: k('email', 'transactional', 'smtp', 'mailbox'),
53
62
  },
54
63
  {
55
64
  module: 'email',
56
65
  skill: 'my-email-verify-api',
57
66
  domain: 'myemailapi.com',
58
- description: 'Sync single-address email verification — syntax + DNS + Microsoft probe. Pre-send quality gate for campaigns.',
67
+ description: 'Sync single-address email verification — syntax + DNS + Microsoft probe. Pre-send quality gate for outbound.',
59
68
  category: 'send',
60
69
  status: 'ga',
61
70
  keywords: k('email', 'verification', 'deliverability', 'syntax', 'dns'),
@@ -175,10 +184,10 @@ exports.SERVICES = [
175
184
  module: 'llm',
176
185
  skill: 'my-llm-api',
177
186
  domain: 'myllmapi.com',
178
- description: 'Provider-agnostic LLM completions + embeddings via MyAPI. Routes to managed models (Gemini today, more soon) at upstream cost. Use in workflow steps, not for your own reasoning.',
187
+ description: 'Self-hosted open-source LLM: raw chat completions (you pick the model) + objective verbs (classify/extract/summarize/draft). Billed in cents per 1M tokens from your balance. Use in workflow steps, not for your own reasoning.',
179
188
  category: 'compute',
180
189
  status: 'ga',
181
- keywords: k('llm', 'completion', 'embedding', 'gemini'),
190
+ keywords: k('llm', 'completion', 'qwen', 'classify'),
182
191
  },
183
192
  {
184
193
  // CLI top-level command is `fn`; SDK namespace is `fn`; skill directory is
@@ -236,7 +245,7 @@ exports.SERVICES = [
236
245
  domain: 'mygitapi.com',
237
246
  description: 'Hosted git repositories over HTTP — create repos, commit files, manage branches and tags, read trees, blobs, history, and diffs. No local clone required.',
238
247
  category: 'store',
239
- status: 'preview',
248
+ status: 'ga',
240
249
  keywords: k('git', 'repository', 'version-control', 'commit', 'scm'),
241
250
  },
242
251
  // ── orchestrate ─────────────────────────────────────────────────────
package/dist/storage.d.ts CHANGED
@@ -7,7 +7,7 @@ export interface Asset {
7
7
  created_at: string;
8
8
  }
9
9
  export declare function ingestAsset(apiKey: string, orgId: string, url: string, name?: string): Promise<Asset>;
10
- export type UploadContentType = 'image/jpeg' | 'image/png' | 'image/gif' | 'image/webp' | 'video/mp4' | 'video/webm';
10
+ export type UploadContentType = 'image/jpeg' | 'image/png' | 'image/gif' | 'image/webp' | 'image/svg+xml' | 'application/pdf' | 'video/mp4' | 'video/webm';
11
11
  export declare function uploadAsset(apiKey: string, orgId: string, file: Blob | Buffer, contentType: UploadContentType, name?: string): Promise<Asset>;
12
12
  export declare function listAssets(apiKey: string, orgId: string): Promise<Asset[]>;
13
13
  export declare function deleteAsset(apiKey: string, orgId: string, assetId: string): Promise<void>;
package/dist/types.d.ts CHANGED
@@ -7,6 +7,9 @@ export interface ApiResponse<T> {
7
7
  latency_ms: number;
8
8
  service: string;
9
9
  version: string;
10
+ next_cursor?: string;
11
+ has_more?: boolean;
12
+ limit?: number;
10
13
  };
11
14
  }
12
15
  export interface PaginatedResponse<T> {
package/dist/webhook.d.ts CHANGED
@@ -8,6 +8,7 @@ export interface WebhookEndpoint {
8
8
  slug: string;
9
9
  url: string;
10
10
  crm_email_path?: string;
11
+ forward_url?: string;
11
12
  created_at: string;
12
13
  }
13
14
  export interface Delivery {
@@ -15,6 +16,9 @@ export interface Delivery {
15
16
  endpoint_id: string;
16
17
  payload: unknown;
17
18
  received_at: string;
19
+ forward_status?: number;
20
+ last_forward_at?: string;
21
+ last_forward_error?: string;
18
22
  }
19
23
  export interface CreateEndpointOptions {
20
24
  description?: string;
@@ -58,6 +58,16 @@ export interface WorkflowRun {
58
58
  started_at?: string;
59
59
  finished_at?: string;
60
60
  created_at: string;
61
+ step_outputs?: unknown[];
62
+ }
63
+ export interface ListRunsOptions {
64
+ limit?: number;
65
+ cursor?: string;
66
+ }
67
+ export interface WorkflowRunsPage {
68
+ runs: WorkflowRun[];
69
+ next_cursor?: string;
70
+ has_more?: boolean;
61
71
  }
62
72
  export declare function createWorkflow(apiKey: string, orgId: string, payload: {
63
73
  name: string;
@@ -78,5 +88,5 @@ export declare function updateWorkflow(apiKey: string, orgId: string, workflowId
78
88
  export declare function enableWorkflow(apiKey: string, orgId: string, workflowId: string): Promise<void>;
79
89
  export declare function disableWorkflow(apiKey: string, orgId: string, workflowId: string): Promise<void>;
80
90
  export declare function deleteWorkflow(apiKey: string, orgId: string, workflowId: string): Promise<void>;
81
- export declare function listWorkflowRuns(apiKey: string, orgId: string, workflowId: string): Promise<WorkflowRun[]>;
91
+ export declare function listWorkflowRuns(apiKey: string, orgId: string, workflowId: string, opts?: ListRunsOptions): Promise<WorkflowRunsPage>;
82
92
  export declare function getWorkflowRun(apiKey: string, orgId: string, runId: string): Promise<WorkflowRun>;
package/dist/workflow.js CHANGED
@@ -72,8 +72,18 @@ async function disableWorkflow(apiKey, orgId, workflowId) {
72
72
  async function deleteWorkflow(apiKey, orgId, workflowId) {
73
73
  return (0, client_1.request)('DELETE', `${config_1.WORKFLOW_BASE}/workflow/orgs/${encodeURIComponent(orgId)}/workflows/${encodeURIComponent(workflowId)}`, apiKey);
74
74
  }
75
- async function listWorkflowRuns(apiKey, orgId, workflowId) {
76
- return (0, client_1.request)('GET', `${config_1.WORKFLOW_BASE}/workflow/orgs/${encodeURIComponent(orgId)}/workflows/${encodeURIComponent(workflowId)}/runs`, apiKey);
75
+ // listWorkflowRuns returns recent runs newest-first. The backend wraps this
76
+ // in a keyset-pagination envelope (limit 1-500 + cursor), so we use
77
+ // requestPage to preserve next_cursor — a plain `request` would drop it.
78
+ async function listWorkflowRuns(apiKey, orgId, workflowId, opts = {}) {
79
+ const qs = new URLSearchParams();
80
+ if (opts.limit !== undefined)
81
+ qs.set('limit', String(opts.limit));
82
+ if (opts.cursor)
83
+ qs.set('cursor', opts.cursor);
84
+ const query = qs.toString() ? `?${qs.toString()}` : '';
85
+ const page = await (0, client_1.requestPage)('GET', `${config_1.WORKFLOW_BASE}/workflow/orgs/${encodeURIComponent(orgId)}/workflows/${encodeURIComponent(workflowId)}/runs${query}`, apiKey);
86
+ return { runs: page.data, next_cursor: page.next_cursor, has_more: page.has_more };
77
87
  }
78
88
  async function getWorkflowRun(apiKey, orgId, runId) {
79
89
  return (0, client_1.request)('GET', `${config_1.WORKFLOW_BASE}/workflow/orgs/${encodeURIComponent(orgId)}/runs/${encodeURIComponent(runId)}`, apiKey);
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@myapihq/sdk",
3
3
  "license": "Apache-2.0",
4
- "version": "1.3.13",
4
+ "version": "2.1.0",
5
5
  "description": "TypeScript SDK for the MyAPI ecosystem",
6
6
  "main": "dist/index.js",
7
7
  "types": "dist/index.d.ts",
package/src/auth.ts ADDED
@@ -0,0 +1,129 @@
1
+ import { request } from './client';
2
+ import { AUTH_BASE as BASE_URL } from './config';
3
+ import type { Exposes } from './exposes';
4
+
5
+ // The end-user auth product (my-auth-api): a managed multi-tenant OIDC
6
+ // Identity Provider for the END USERS of apps built on MyAPI. Distinct from
7
+ // operator/account auth (hq/account/*). Each org gets one auth tenant; OIDC
8
+ // clients (apps) register against it. End users sign in with managed Google,
9
+ // email/password, or magic links (per the tenant's `connections`). Tokens are
10
+ // RS256, verified via the tenant's JWKS.
11
+ //
12
+ // The browser/app-facing OIDC + sign-in flows — discovery, jwks, authorize,
13
+ // token, userinfo, password/magic/google login, verify-email, password reset,
14
+ // the hosted login page — are machine/end-user surfaces consumed by apps, the
15
+ // JS SDK, and browsers, NOT management calls; they are not CLI/SDK verbs. This
16
+ // module covers only the org-scoped MANAGEMENT surface: tenant, clients, MAU
17
+ // usage, and the custom auth domain.
18
+ export const EXPOSES: Exposes = [
19
+ 'POST /auth/orgs/{org_id}/tenant',
20
+ 'GET /auth/orgs/{org_id}/tenant',
21
+ 'POST /auth/orgs/{org_id}/clients',
22
+ 'GET /auth/orgs/{org_id}/clients',
23
+ 'GET /auth/orgs/{org_id}/usage',
24
+ 'POST /auth/orgs/{org_id}/domain',
25
+ 'GET /auth/orgs/{org_id}/domain',
26
+ 'DELETE /auth/orgs/{org_id}/domain',
27
+ ];
28
+
29
+ // Sign-in methods a tenant offers on its hosted login page.
30
+ export type AuthConnection = 'google' | 'password' | 'magic';
31
+
32
+ export interface Tenant {
33
+ tenant_id: string;
34
+ issuer: string; // OIDC issuer: <base>/<tenant_id>
35
+ login_url: string; // hosted authorize endpoint
36
+ connections?: AuthConnection[];
37
+ }
38
+
39
+ export interface CreateTenantInput {
40
+ // Sign-in methods to enable. Defaults to ['google'] when omitted.
41
+ connections?: AuthConnection[];
42
+ // Opaque JSON for the hosted login page (branding).
43
+ theme?: unknown;
44
+ }
45
+
46
+ export interface AuthClient {
47
+ client_id: string;
48
+ name?: string;
49
+ type: 'spa' | 'web';
50
+ redirect_uris: string[];
51
+ issuer?: string;
52
+ // Returned exactly ONCE, on create, for confidential ('web') clients.
53
+ // SPA clients are public and have no secret.
54
+ client_secret?: string;
55
+ created_at?: string;
56
+ }
57
+
58
+ export interface ListClientsResponse {
59
+ clients: AuthClient[];
60
+ }
61
+
62
+ export interface CreateClientInput {
63
+ name: string;
64
+ type: 'spa' | 'web';
65
+ redirect_uris: string[];
66
+ }
67
+
68
+ // Monthly-active-user metering for the tenant (auth is billed per MAU).
69
+ export interface AuthUsage {
70
+ period: string; // 'YYYY-MM'
71
+ active_users: number;
72
+ price_cents_each: number;
73
+ }
74
+
75
+ // A custom auth domain (e.g. auth.acme.com) for the tenant's hosted login + issuer.
76
+ export interface AuthDomain {
77
+ domain: string;
78
+ status: 'pending' | 'active';
79
+ dns: { type: string; name: string; value: string };
80
+ next?: string;
81
+ issuer?: string; // present once active
82
+ login_url?: string; // present once active
83
+ }
84
+
85
+ // Create (or update) the org's auth tenant. Idempotent. `connections` selects
86
+ // the sign-in methods (subset of google/password/magic; defaults to google);
87
+ // `theme` is opaque JSON for the hosted login page.
88
+ export async function createTenant(apiKey: string, orgId: string, input: CreateTenantInput = {}): Promise<Tenant> {
89
+ const body: Record<string, unknown> = {};
90
+ if (input.connections !== undefined) body.connections = input.connections;
91
+ if (input.theme !== undefined) body.theme = input.theme;
92
+ return request('POST', `${BASE_URL}/auth/orgs/${encodeURIComponent(orgId)}/tenant`, apiKey, body);
93
+ }
94
+
95
+ // Fetch the org's auth tenant. Rejects with TENANT_NOT_FOUND if not created.
96
+ export async function getTenant(apiKey: string, orgId: string): Promise<Tenant> {
97
+ return request('GET', `${BASE_URL}/auth/orgs/${encodeURIComponent(orgId)}/tenant`, apiKey);
98
+ }
99
+
100
+ // Register an OIDC client (an app) under the org's tenant. Auto-provisions the
101
+ // tenant if absent. For type 'web' the response carries `client_secret` once.
102
+ export async function createClient(apiKey: string, orgId: string, input: CreateClientInput): Promise<AuthClient> {
103
+ return request('POST', `${BASE_URL}/auth/orgs/${encodeURIComponent(orgId)}/clients`, apiKey, input);
104
+ }
105
+
106
+ export async function listClients(apiKey: string, orgId: string): Promise<ListClientsResponse> {
107
+ return request('GET', `${BASE_URL}/auth/orgs/${encodeURIComponent(orgId)}/clients`, apiKey);
108
+ }
109
+
110
+ // Monthly-active-user usage for the current period (auth is billed per MAU).
111
+ export async function getUsage(apiKey: string, orgId: string): Promise<AuthUsage> {
112
+ return request('GET', `${BASE_URL}/auth/orgs/${encodeURIComponent(orgId)}/usage`, apiKey);
113
+ }
114
+
115
+ // Set a custom auth domain (e.g. auth.acme.com). Returns the DNS record to
116
+ // create; TLS provisions automatically and the domain becomes the issuer once
117
+ // active. One custom domain per org.
118
+ export async function registerDomain(apiKey: string, orgId: string, domain: string): Promise<AuthDomain> {
119
+ return request('POST', `${BASE_URL}/auth/orgs/${encodeURIComponent(orgId)}/domain`, apiKey, { domain });
120
+ }
121
+
122
+ // Fetch the org's custom auth domain. Rejects with NO_DOMAIN if none set.
123
+ export async function getDomain(apiKey: string, orgId: string): Promise<AuthDomain> {
124
+ return request('GET', `${BASE_URL}/auth/orgs/${encodeURIComponent(orgId)}/domain`, apiKey);
125
+ }
126
+
127
+ export async function deleteDomain(apiKey: string, orgId: string, domain: string): Promise<{ domain: string; status: string }> {
128
+ return request('DELETE', `${BASE_URL}/auth/orgs/${encodeURIComponent(orgId)}/domain`, apiKey, { domain });
129
+ }
package/src/client.ts CHANGED
@@ -12,13 +12,13 @@ export class MyApiError extends Error {
12
12
  }
13
13
  }
14
14
 
15
- export async function request<T>(
15
+ async function requestFull<T>(
16
16
  method: string,
17
17
  url: string,
18
18
  apiKey?: string,
19
19
  body?: unknown,
20
20
  extraHeaders?: Record<string, string>,
21
- ): Promise<T> {
21
+ ): Promise<ApiResponse<T>> {
22
22
  const headers: Record<string, string> = {};
23
23
 
24
24
  if (apiKey) {
@@ -48,7 +48,7 @@ export async function request<T>(
48
48
  const response = await fetch(url, options);
49
49
 
50
50
  if (response.status === 204) {
51
- return undefined as T;
51
+ return { success: true, data: null, error: null, meta: {} as ApiResponse<T>['meta'] };
52
52
  }
53
53
 
54
54
  // Read the body as text first so we can include a snippet in errors when
@@ -85,5 +85,37 @@ export async function request<T>(
85
85
  throw new MyApiError(code, response.status, detail, errBody);
86
86
  }
87
87
 
88
- return apiResponse.data as T;
88
+ return apiResponse;
89
+ }
90
+
91
+ // Returns the unwrapped `data` payload — the common case.
92
+ export async function request<T>(
93
+ method: string,
94
+ url: string,
95
+ apiKey?: string,
96
+ body?: unknown,
97
+ extraHeaders?: Record<string, string>,
98
+ ): Promise<T> {
99
+ return (await requestFull<T>(method, url, apiKey, body, extraHeaders)).data as T;
100
+ }
101
+
102
+ export interface Page<T> {
103
+ data: T[];
104
+ next_cursor?: string;
105
+ has_more?: boolean;
106
+ }
107
+
108
+ // For keyset-paginated endpoints (backend `envelope.WrapPage`): `data` is a
109
+ // bare array and the cursor/has_more live under `meta`. `request` would drop
110
+ // the cursor, so list endpoints with pagination must use this.
111
+ export async function requestPage<T>(
112
+ method: string,
113
+ url: string,
114
+ apiKey?: string,
115
+ body?: unknown,
116
+ extraHeaders?: Record<string, string>,
117
+ ): Promise<Page<T>> {
118
+ const r = await requestFull<T[]>(method, url, apiKey, body, extraHeaders);
119
+ const meta = (r.meta ?? {}) as { next_cursor?: string; has_more?: boolean };
120
+ return { data: (r.data ?? []) as T[], next_cursor: meta.next_cursor, has_more: meta.has_more };
89
121
  }
package/src/config.ts CHANGED
@@ -36,3 +36,4 @@ export const AUDIENCE_BASE= process.env.MYAPI_AUDIENCE_URL?? GATEWAY;
36
36
  export const LLM_BASE = process.env.MYAPI_LLM_URL ?? GATEWAY;
37
37
  export const DATABASE_BASE= process.env.MYAPI_DATABASE_URL?? GATEWAY;
38
38
  export const CRM_BASE = process.env.MYAPI_CRM_URL ?? GATEWAY;
39
+ export const AUTH_BASE = process.env.MYAPI_AUTH_URL ?? GATEWAY;
package/src/container.ts CHANGED
@@ -1,4 +1,5 @@
1
- import { request } from './client';
1
+ import { request, MyApiError } from './client';
2
+ import { ApiResponse } from './types';
2
3
  import { CONTAINER_BASE as BASE_URL } from './config';
3
4
  import type { Exposes } from './exposes';
4
5
 
@@ -39,6 +40,9 @@ export interface Container {
39
40
  // Origin Rule that rewrites Host + SNI to the Cloud Run hostname.
40
41
  custom_domain?: string;
41
42
  status: string;
43
+ // Egress mode for the container's outbound traffic (backend returns it on
44
+ // every row, container/crud.go). Static-IP vs. default depending on config.
45
+ egress: string;
42
46
  created_at: string;
43
47
  updated_at: string;
44
48
  }
@@ -89,12 +93,58 @@ export async function deleteContainer(apiKey: string, orgId: string, containerId
89
93
  return request('DELETE', `${BASE_URL}/container/orgs/${encodeURIComponent(orgId)}/containers/${encodeURIComponent(containerId)}`, apiKey);
90
94
  }
91
95
 
92
- // deployContainer ships a revision from a pre-built image ref to Cloud Run
93
- // (source builds land in a later backend slice). Rotates the scoped key.
96
+ // deployContainer ships a revision from a pre-built image ref to Cloud Run.
97
+ // Synchronous returns status='active' with a rotated scoped key.
94
98
  export async function deployContainer(apiKey: string, orgId: string, containerId: string, image: string): Promise<DeployResponse> {
95
99
  return request('POST', `${BASE_URL}/container/orgs/${encodeURIComponent(orgId)}/containers/${encodeURIComponent(containerId)}/deploy`, apiKey, { image });
96
100
  }
97
101
 
102
+ // BuildDeployResponse — the async source-build path. The backend records a
103
+ // `building` revision, kicks off Cloud Build on a background goroutine, and
104
+ // returns immediately. The scoped key is minted later (once the image
105
+ // exists), so it is NOT in this response. Poll getContainer until
106
+ // status='active' (or 'build_error'/'deploy_error').
107
+ export interface BuildDeployResponse {
108
+ container_id: string;
109
+ revision_id: string;
110
+ status: string; // 'building'
111
+ message?: string;
112
+ }
113
+
114
+ // deployContainerSource uploads a build-context tarball via multipart `source`
115
+ // (backend deploy.go builds it via Cloud Build → Artifact Registry, then
116
+ // deploys). Asynchronous: returns status='building' to poll. The tarball is
117
+ // capped at 100MB server-side. Mirrors the multipart upload in storage.ts.
118
+ export async function deployContainerSource(
119
+ apiKey: string,
120
+ orgId: string,
121
+ containerId: string,
122
+ tarball: Blob | Buffer,
123
+ filename = 'source.tar.gz',
124
+ ): Promise<BuildDeployResponse> {
125
+ const formData = new FormData();
126
+ formData.append('source', new Blob([tarball as any], { type: 'application/gzip' }), filename);
127
+
128
+ const response = await fetch(
129
+ `${BASE_URL}/container/orgs/${encodeURIComponent(orgId)}/containers/${encodeURIComponent(containerId)}/deploy`,
130
+ { method: 'POST', headers: { Authorization: `Bearer ${apiKey}` }, body: formData as any },
131
+ );
132
+
133
+ let result: any;
134
+ try {
135
+ result = await response.json();
136
+ } catch {
137
+ throw new MyApiError('invalid_json_response', response.status);
138
+ }
139
+ if (!response.ok || !result?.success) {
140
+ const err = result?.error;
141
+ const code = typeof err === 'object' ? (err?.code || 'unknown_error') : (err || 'unknown_error');
142
+ const detail = typeof err === 'object' ? err?.message : undefined;
143
+ throw new MyApiError(code, response.status, detail, typeof err === 'object' ? err : undefined);
144
+ }
145
+ return (result as ApiResponse<BuildDeployResponse>).data as BuildDeployResponse;
146
+ }
147
+
98
148
  // One runtime log line from the container's Cloud Run resource.
99
149
  export interface LogEntry {
100
150
  timestamp: string;
package/src/crm.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { request } from './client';
1
+ import { request, requestPage } from './client';
2
2
  import { CRM_BASE as BASE_URL } from './config';
3
3
  import type { Exposes } from './exposes';
4
4
 
@@ -46,7 +46,8 @@ export type EventKind =
46
46
  | 'email_clicked'
47
47
  | 'email_replied'
48
48
  | 'pixel_visit'
49
- | 'webhook_received';
49
+ | 'webhook_received'
50
+ | 'payment';
50
51
 
51
52
  // Goldfox enrichment is a live join — the response embeds the current
52
53
  // Goldfox row when goldfox_person_id is set. v1: returns null until the
@@ -204,7 +205,10 @@ export async function getContactEvents(apiKey: string, orgId: string, id: string
204
205
  if (opts.kind != null) qs.append('kind', opts.kind);
205
206
  const q = qs.toString();
206
207
  const url = `${BASE_URL}/crm/orgs/${encodeURIComponent(orgId)}/contacts/${encodeURIComponent(id)}/events${q ? `?${q}` : ''}`;
207
- return request('GET', url, apiKey);
208
+ // Paginated (WrapPage): events are the bare `data` array with the cursor
209
+ // under `meta` — use requestPage so `next_cursor` survives.
210
+ const page = await requestPage<ContactEvent>('GET', url, apiKey);
211
+ return { events: page.data, next_cursor: page.next_cursor };
208
212
  }
209
213
 
210
214
  // ── Companies ─────────────────────────────────────────────────────────────
package/src/database.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { request } from './client';
1
+ import { request, requestPage } from './client';
2
2
  import { DATABASE_BASE as BASE_URL } from './config';
3
3
  import type { Exposes } from './exposes';
4
4
 
@@ -71,7 +71,10 @@ export async function listKeys(apiKey: string, orgId: string, ns: string, opts:
71
71
  if (opts.values) qs.append('values', 'true');
72
72
  const q = qs.toString();
73
73
  const url = `${BASE_URL}/database/orgs/${encodeURIComponent(orgId)}/namespaces/${encodeURIComponent(ns)}/keys${q ? `?${q}` : ''}`;
74
- return request('GET', url, apiKey);
74
+ // Paginated (WrapPage): keys come back as the bare `data` array with the
75
+ // cursor under `meta` — must use requestPage or the cursor is lost.
76
+ const page = await requestPage<KeyEntry>('GET', url, apiKey);
77
+ return { keys: page.data, next_cursor: page.next_cursor };
75
78
  }
76
79
 
77
80
  export async function getKey(apiKey: string, orgId: string, ns: string, key: string): Promise<KeyEntry> {