@myapihq/sdk 1.2.2 → 1.2.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/client.d.ts CHANGED
@@ -2,6 +2,7 @@ export declare class MyApiError extends Error {
2
2
  code: string;
3
3
  status: number;
4
4
  detail?: string | undefined;
5
- constructor(code: string, status: number, detail?: string | undefined);
5
+ body?: Record<string, unknown> | undefined;
6
+ constructor(code: string, status: number, detail?: string | undefined, body?: Record<string, unknown> | undefined);
6
7
  }
7
8
  export declare function request<T>(method: string, url: string, apiKey?: string, body?: unknown, extraHeaders?: Record<string, string>): Promise<T>;
package/dist/client.js CHANGED
@@ -6,13 +6,18 @@ class MyApiError extends Error {
6
6
  code;
7
7
  status;
8
8
  detail;
9
- constructor(code, status, detail) {
9
+ body;
10
+ // `body` is the full error object as returned by the backend — preserved so
11
+ // callers can read service-specific fields the SDK doesn't otherwise model
12
+ // (e.g. CF_API_ERROR carries `cf_message` / `cf_status`).
13
+ constructor(code, status, detail, body) {
10
14
  // Surface the detail in the message so console.error / err.message
11
15
  // shows useful info without callers having to look at err.detail.
12
16
  super(detail ? `${code} (${status}): ${detail}` : code);
13
17
  this.code = code;
14
18
  this.status = status;
15
19
  this.detail = detail;
20
+ this.body = body;
16
21
  this.name = 'MyApiError';
17
22
  }
18
23
  }
@@ -59,14 +64,16 @@ async function request(method, url, apiKey, body, extraHeaders) {
59
64
  const err = result?.error;
60
65
  const code = typeof err === 'object' ? (err?.code || 'unknown_error') : (err || 'unknown_error');
61
66
  const detail = typeof err === 'object' ? (err?.message || undefined) : undefined;
62
- throw new MyApiError(code, response.status, detail);
67
+ const body = typeof err === 'object' ? err : undefined;
68
+ throw new MyApiError(code, response.status, detail, body);
63
69
  }
64
70
  const apiResponse = result;
65
71
  if (!apiResponse.success) {
66
72
  const err = apiResponse.error;
67
73
  const code = typeof err === 'object' ? (err?.code || 'unknown_error') : (err || 'unknown_error');
68
74
  const detail = typeof err === 'object' ? (err?.message || undefined) : undefined;
69
- throw new MyApiError(code, response.status, detail);
75
+ const body = typeof err === 'object' ? err : undefined;
76
+ throw new MyApiError(code, response.status, detail, body);
70
77
  }
71
78
  return apiResponse.data;
72
79
  }
package/dist/config.d.ts CHANGED
@@ -1,6 +1,7 @@
1
1
  export declare const HQ_BASE: string;
2
2
  export declare const DOMAIN_BASE: string;
3
3
  export declare const FUNNEL_BASE: string;
4
+ export declare const FUNCTION_BASE: string;
4
5
  export declare const IMAGE_BASE: string;
5
6
  export declare const WEBHOOK_BASE: string;
6
7
  export declare const WORKFLOW_BASE: string;
package/dist/config.js CHANGED
@@ -8,11 +8,14 @@
8
8
  // local dev — `source scripts/local-env.sh` still points everything at
9
9
  // localhost:8080 regardless of the prod default.
10
10
  Object.defineProperty(exports, "__esModule", { value: true });
11
- exports.CRM_BASE = exports.DATABASE_BASE = exports.LLM_BASE = exports.AUDIENCE_BASE = exports.COMPANY_BASE = exports.PEOPLE_BASE = exports.PIXEL_BASE = exports.URL_BASE = exports.STORAGE_BASE = exports.EMAIL_BASE = exports.WORKFLOW_BASE = exports.WEBHOOK_BASE = exports.IMAGE_BASE = exports.FUNNEL_BASE = exports.DOMAIN_BASE = exports.HQ_BASE = void 0;
11
+ exports.CRM_BASE = exports.DATABASE_BASE = exports.LLM_BASE = exports.AUDIENCE_BASE = exports.COMPANY_BASE = exports.PEOPLE_BASE = exports.PIXEL_BASE = exports.URL_BASE = exports.STORAGE_BASE = exports.EMAIL_BASE = exports.WORKFLOW_BASE = exports.WEBHOOK_BASE = exports.IMAGE_BASE = exports.FUNCTION_BASE = exports.FUNNEL_BASE = exports.DOMAIN_BASE = exports.HQ_BASE = void 0;
12
12
  const GATEWAY = 'https://api.myapihq.com';
13
13
  exports.HQ_BASE = process.env.MYAPI_HQ_URL ?? process.env.MYAPI_API_BASE ?? GATEWAY;
14
14
  exports.DOMAIN_BASE = process.env.MYAPI_DOMAIN_URL ?? 'https://api.mydomainapi.com';
15
15
  exports.FUNNEL_BASE = process.env.MYAPI_FUNNEL_URL ?? 'https://api.myfunnelapi.com';
16
+ // New slots — routed via the gateway until brand hosts are DNS-wired
17
+ // (mirrors the LLM/DATABASE/CRM pattern below).
18
+ exports.FUNCTION_BASE = process.env.MYAPI_FUNCTION_URL ?? GATEWAY;
16
19
  exports.IMAGE_BASE = process.env.MYAPI_IMAGE_URL ?? 'https://api.myimageapi.com';
17
20
  exports.WEBHOOK_BASE = process.env.MYAPI_WEBHOOK_URL ?? 'https://api.mywebhookapi.com';
18
21
  exports.WORKFLOW_BASE = process.env.MYAPI_WORKFLOW_URL ?? 'https://api.myworkflowapi.com';
package/dist/domain.d.ts CHANGED
@@ -6,6 +6,17 @@ export interface DomainRecord {
6
6
  expires_at?: string;
7
7
  days_until_expiry?: number;
8
8
  email_infra_ready?: boolean;
9
+ email_infra?: 'skipped' | 'pending' | 'ready' | 'error';
10
+ email_subdomain?: string;
11
+ dns_active?: boolean;
12
+ steps_completed?: string[];
13
+ error_detail?: {
14
+ failed_step: string;
15
+ message: string;
16
+ retryable: boolean;
17
+ attempt_count?: number;
18
+ last_attempt_at?: string;
19
+ };
9
20
  org_id?: string | null;
10
21
  created_at: string;
11
22
  }
@@ -56,8 +67,17 @@ export interface ImportDomainResponse {
56
67
  export declare function importDomain(apiKey: string, orgId: string, domain: string): Promise<ImportDomainResponse>;
57
68
  export declare function listDomains(apiKey: string, orgId: string, filter?: string): Promise<DomainRecord[]>;
58
69
  export declare function getDomainStatus(apiKey: string, orgId: string, domain: string): Promise<DomainRecord>;
59
- export declare function assignDomain(apiKey: string, orgId: string, domain: string): Promise<DomainRecord>;
60
- export declare function unassignDomain(apiKey: string, orgId: string, domain: string): Promise<DomainRecord>;
70
+ export interface AssignDomainResponse {
71
+ domain: string;
72
+ org_id: string | null;
73
+ include_www: boolean;
74
+ routes_bound: string[];
75
+ }
76
+ export declare function assignDomain(apiKey: string, orgId: string, domain: string, opts?: {
77
+ includeWww?: boolean;
78
+ funnelId?: string;
79
+ }): Promise<AssignDomainResponse>;
80
+ export declare function unassignDomain(apiKey: string, orgId: string, domain: string): Promise<AssignDomainResponse>;
61
81
  export declare function getDomainSettings(apiKey: string, orgId: string, domain: string): Promise<DomainSettings>;
62
82
  export type DnsRecordType = 'A' | 'AAAA' | 'CNAME' | 'MX' | 'TXT';
63
83
  export interface DnsRecord {
@@ -84,6 +104,19 @@ export declare function getDnsRecord(apiKey: string, orgId: string, domain: stri
84
104
  export declare function createDnsRecord(apiKey: string, orgId: string, domain: string, input: DnsRecordInput): Promise<DnsRecord>;
85
105
  export declare function updateDnsRecord(apiKey: string, orgId: string, domain: string, recordId: string, input: Partial<Omit<DnsRecordInput, 'type'>>): Promise<DnsRecord>;
86
106
  export declare function deleteDnsRecord(apiKey: string, orgId: string, domain: string, recordId: string): Promise<void>;
107
+ export interface EmailInfraResponse {
108
+ domain: string;
109
+ email_infra: 'pending' | 'ready' | 'error';
110
+ email_subdomain: string;
111
+ next_step?: string;
112
+ }
113
+ export declare function setupEmailInfra(apiKey: string, orgId: string, domain: string, subdomain?: string): Promise<EmailInfraResponse>;
114
+ export interface RetryProvisioningResponse {
115
+ domain: string;
116
+ email_infra?: string;
117
+ next_step?: string;
118
+ }
119
+ export declare function retryProvisioning(apiKey: string, orgId: string, domain: string): Promise<RetryProvisioningResponse>;
87
120
  export declare function updateDomainSettings(apiKey: string, orgId: string, domain: string, payload: {
88
121
  security_level?: 'essentially_off' | 'medium' | 'high' | 'under_attack';
89
122
  browser_check?: 'on' | 'off';
package/dist/domain.js CHANGED
@@ -15,6 +15,8 @@ exports.getDnsRecord = getDnsRecord;
15
15
  exports.createDnsRecord = createDnsRecord;
16
16
  exports.updateDnsRecord = updateDnsRecord;
17
17
  exports.deleteDnsRecord = deleteDnsRecord;
18
+ exports.setupEmailInfra = setupEmailInfra;
19
+ exports.retryProvisioning = retryProvisioning;
18
20
  exports.updateDomainSettings = updateDomainSettings;
19
21
  const client_1 = require("./client");
20
22
  const config_1 = require("./config");
@@ -33,6 +35,8 @@ exports.EXPOSES = [
33
35
  'POST /domain/orgs/{org_id}/{domain}/records',
34
36
  'PATCH /domain/orgs/{org_id}/{domain}/records/{record_id}',
35
37
  'DELETE /domain/orgs/{org_id}/{domain}/records/{record_id}',
38
+ 'POST /domain/orgs/{org_id}/{domain}/email-infra',
39
+ 'POST /domain/orgs/{org_id}/{domain}/retry-provisioning',
36
40
  ];
37
41
  async function checkDomain(apiKey, orgId, domain) {
38
42
  return (0, client_1.request)('GET', `${config_1.DOMAIN_BASE}/domain/orgs/${encodeURIComponent(orgId)}/check/available/${encodeURIComponent(domain)}`, apiKey);
@@ -57,8 +61,18 @@ async function listDomains(apiKey, orgId, filter) {
57
61
  async function getDomainStatus(apiKey, orgId, domain) {
58
62
  return (0, client_1.request)('GET', `${config_1.DOMAIN_BASE}/domain/orgs/${encodeURIComponent(orgId)}/${encodeURIComponent(domain)}/status`, apiKey);
59
63
  }
60
- async function assignDomain(apiKey, orgId, domain) {
61
- return (0, client_1.request)('POST', `${config_1.DOMAIN_BASE}/domain/orgs/${encodeURIComponent(orgId)}/${encodeURIComponent(domain)}/assign`, apiKey, { org_id: orgId });
64
+ async function assignDomain(apiKey, orgId, domain, opts) {
65
+ const body = { org_id: orgId };
66
+ if (opts?.includeWww === false)
67
+ body.include_www = false;
68
+ // Backend (2026-05-15): optional funnel_id picks which funnel the domain
69
+ // is bound to. Defaults server-side to the org's only funnel (or first
70
+ // when ambiguous, today; will become required once N-funnels-per-org lands
71
+ // for real). Pre-emptively threading it through gives agents a way to be
72
+ // explicit when the migration completes without an SDK shape change.
73
+ if (opts?.funnelId)
74
+ body.funnel_id = opts.funnelId;
75
+ return (0, client_1.request)('POST', `${config_1.DOMAIN_BASE}/domain/orgs/${encodeURIComponent(orgId)}/${encodeURIComponent(domain)}/assign`, apiKey, body);
62
76
  }
63
77
  async function unassignDomain(apiKey, orgId, domain) {
64
78
  return (0, client_1.request)('POST', `${config_1.DOMAIN_BASE}/domain/orgs/${encodeURIComponent(orgId)}/${encodeURIComponent(domain)}/assign`, apiKey, { org_id: null });
@@ -82,6 +96,13 @@ async function updateDnsRecord(apiKey, orgId, domain, recordId, input) {
82
96
  async function deleteDnsRecord(apiKey, orgId, domain, recordId) {
83
97
  return (0, client_1.request)('DELETE', `${config_1.DOMAIN_BASE}/domain/orgs/${encodeURIComponent(orgId)}/${encodeURIComponent(domain)}/records/${encodeURIComponent(recordId)}`, apiKey);
84
98
  }
99
+ async function setupEmailInfra(apiKey, orgId, domain, subdomain) {
100
+ const body = subdomain ? { subdomain } : {};
101
+ return (0, client_1.request)('POST', `${config_1.DOMAIN_BASE}/domain/orgs/${encodeURIComponent(orgId)}/${encodeURIComponent(domain)}/email-infra`, apiKey, body);
102
+ }
103
+ async function retryProvisioning(apiKey, orgId, domain) {
104
+ return (0, client_1.request)('POST', `${config_1.DOMAIN_BASE}/domain/orgs/${encodeURIComponent(orgId)}/${encodeURIComponent(domain)}/retry-provisioning`, apiKey);
105
+ }
85
106
  async function updateDomainSettings(apiKey, orgId, domain, payload) {
86
107
  return (0, client_1.request)('POST', `${config_1.DOMAIN_BASE}/domain/orgs/${encodeURIComponent(orgId)}/${encodeURIComponent(domain)}/settings`, apiKey, payload);
87
108
  }
@@ -0,0 +1,27 @@
1
+ import type { Exposes } from './exposes';
2
+ export declare const EXPOSES: Exposes;
3
+ export type TriggerType = 'http' | 'cron';
4
+ export interface Fn {
5
+ id: string;
6
+ org_id: string;
7
+ name: string;
8
+ trigger_type: TriggerType;
9
+ cron_schedule?: string;
10
+ invocation_url: string;
11
+ created_at: string;
12
+ updated_at: string;
13
+ }
14
+ export interface CreatePayload {
15
+ name: string;
16
+ trigger_type?: TriggerType;
17
+ cron_schedule?: string;
18
+ }
19
+ export interface CreateResponse {
20
+ function: Fn;
21
+ scoped_api_key: string;
22
+ scoped_api_key_id: string;
23
+ }
24
+ export declare function createFunction(apiKey: string, orgId: string, payload: CreatePayload): Promise<CreateResponse>;
25
+ export declare function listFunctions(apiKey: string, orgId: string): Promise<Fn[]>;
26
+ export declare function getFunction(apiKey: string, orgId: string, fnId: string): Promise<Fn>;
27
+ export declare function deleteFunction(apiKey: string, orgId: string, fnId: string): Promise<void>;
@@ -0,0 +1,32 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.EXPOSES = void 0;
4
+ exports.createFunction = createFunction;
5
+ exports.listFunctions = listFunctions;
6
+ exports.getFunction = getFunction;
7
+ exports.deleteFunction = deleteFunction;
8
+ const client_1 = require("./client");
9
+ const config_1 = require("./config");
10
+ // Backend: Story 1 (per myapi-hq/internal/routes/function/crud.go). Metadata
11
+ // + scoped API key issuance only — Cloudflare Worker upload, /logs, and /env
12
+ // land in Story 2-5. The SDK surface stays minimal until those endpoints
13
+ // exist; do not add forward-compat fields here that the backend will silently
14
+ // ignore.
15
+ exports.EXPOSES = [
16
+ 'POST /function/orgs/{org_id}/functions',
17
+ 'GET /function/orgs/{org_id}/functions',
18
+ 'GET /function/orgs/{org_id}/functions/{id}',
19
+ 'DELETE /function/orgs/{org_id}/functions/{id}',
20
+ ];
21
+ async function createFunction(apiKey, orgId, payload) {
22
+ return (0, client_1.request)('POST', `${config_1.FUNCTION_BASE}/function/orgs/${encodeURIComponent(orgId)}/functions`, apiKey, payload);
23
+ }
24
+ async function listFunctions(apiKey, orgId) {
25
+ return (0, client_1.request)('GET', `${config_1.FUNCTION_BASE}/function/orgs/${encodeURIComponent(orgId)}/functions`, apiKey);
26
+ }
27
+ async function getFunction(apiKey, orgId, fnId) {
28
+ return (0, client_1.request)('GET', `${config_1.FUNCTION_BASE}/function/orgs/${encodeURIComponent(orgId)}/functions/${encodeURIComponent(fnId)}`, apiKey);
29
+ }
30
+ async function deleteFunction(apiKey, orgId, fnId) {
31
+ return (0, client_1.request)('DELETE', `${config_1.FUNCTION_BASE}/function/orgs/${encodeURIComponent(orgId)}/functions/${encodeURIComponent(fnId)}`, apiKey);
32
+ }
package/dist/funnel.d.ts CHANGED
@@ -3,6 +3,7 @@ export declare const EXPOSES: Exposes;
3
3
  export interface Funnel {
4
4
  id: string;
5
5
  org_id: string;
6
+ name?: string;
6
7
  created_at: string;
7
8
  updated_at: string;
8
9
  }
@@ -24,7 +25,9 @@ export interface VerifyTest {
24
25
  passed: boolean;
25
26
  details: string;
26
27
  }
27
- export declare function createFunnel(apiKey: string, orgId: string): Promise<CreateFunnelResponse>;
28
+ export declare function createFunnel(apiKey: string, orgId: string, opts?: {
29
+ name?: string;
30
+ }): Promise<CreateFunnelResponse>;
28
31
  export declare function getFunnel(apiKey: string, orgId: string, funnelId: string): Promise<Funnel>;
29
32
  export declare function listFunnels(apiKey: string, orgId: string): Promise<Funnel[]>;
30
33
  export declare function deleteFunnel(apiKey: string, orgId: string, funnelId: string): Promise<void>;
package/dist/funnel.js CHANGED
@@ -19,8 +19,10 @@ exports.EXPOSES = [
19
19
  'POST /funnel/orgs/{org_id}/funnels/{funnel_id}/verify',
20
20
  'GET /funnel/orgs/{org_id}/funnels/{funnel_id}/pages',
21
21
  ];
22
- async function createFunnel(apiKey, orgId) {
23
- return (0, client_1.request)('POST', `${config_1.FUNNEL_BASE}/funnel/orgs/${encodeURIComponent(orgId)}/funnels`, apiKey, {});
22
+ async function createFunnel(apiKey, orgId, opts) {
23
+ // v2: accept optional name for N-funnels-per-org. Backend rejects names
24
+ // matching the reserved-suffix rules; agent should pre-validate.
25
+ return (0, client_1.request)('POST', `${config_1.FUNNEL_BASE}/funnel/orgs/${encodeURIComponent(orgId)}/funnels`, apiKey, opts?.name ? { name: opts.name } : {});
24
26
  }
25
27
  async function getFunnel(apiKey, orgId, funnelId) {
26
28
  return (0, client_1.request)('GET', `${config_1.FUNNEL_BASE}/funnel/orgs/${encodeURIComponent(orgId)}/funnels/${encodeURIComponent(funnelId)}`, apiKey);
package/dist/index.d.ts CHANGED
@@ -16,3 +16,4 @@ export * as audience from './audience';
16
16
  export * as llm from './llm';
17
17
  export * as database from './database';
18
18
  export * as crm from './crm';
19
+ export * as fn from './function';
package/dist/index.js CHANGED
@@ -36,7 +36,7 @@ var __importStar = (this && this.__importStar) || (function () {
36
36
  };
37
37
  })();
38
38
  Object.defineProperty(exports, "__esModule", { value: true });
39
- 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.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;
40
40
  __exportStar(require("./types"), exports);
41
41
  __exportStar(require("./client"), exports);
42
42
  // Note: config constants (STORAGE_BASE etc.) are NOT re-exported from the
@@ -61,3 +61,4 @@ exports.audience = __importStar(require("./audience"));
61
61
  exports.llm = __importStar(require("./llm"));
62
62
  exports.database = __importStar(require("./database"));
63
63
  exports.crm = __importStar(require("./crm"));
64
+ exports.fn = __importStar(require("./function"));
package/dist/services.js CHANGED
@@ -187,4 +187,21 @@ exports.SERVICES = [
187
187
  status: 'ga',
188
188
  keywords: k('llm', 'completion', 'embedding', 'gemini'),
189
189
  },
190
+ {
191
+ // CLI top-level command is `fn`; SDK namespace is `fn`; skill directory is
192
+ // `my-function-api` (the agent-discoverable brand name).
193
+ //
194
+ // Backend status: Story 1 shipped (metadata + scoped API key issuance).
195
+ // Story 2 (CF Workers upload, invocation URL), Story 4 (/logs), Story 5
196
+ // (/env) pending. 'planned' reflects "partial backend, not stable GA."
197
+ module: 'fn',
198
+ skill: 'my-function-api',
199
+ domain: 'myfunctionapi.com',
200
+ description: 'Run JavaScript functions on the edge. HTTP or cron triggers. Pre-injected MYAPI SDK scoped to the org — no auth tokens in user code.',
201
+ category: 'compute',
202
+ status: 'planned',
203
+ keywords: k('function', 'serverless', 'edge', 'cloudflare-workers', 'http-handler', 'cron'),
204
+ },
205
+ // Note: my-payments-api removed from registry — backend has no /payments/*
206
+ // endpoints yet. Re-add when backend ships per tech-spec-my-payments-api-2026-05-14.md.
190
207
  ];
package/dist/webhook.d.ts CHANGED
@@ -19,8 +19,16 @@ export interface Delivery {
19
19
  export interface CreateEndpointOptions {
20
20
  description?: string;
21
21
  crm_email_path?: string;
22
+ forward_url?: string;
23
+ }
24
+ export interface UpdateEndpointPayload {
25
+ name?: string;
26
+ description?: string;
27
+ crm_email_path?: string;
28
+ forward_url?: string;
22
29
  }
23
30
  export declare function createEndpoint(apiKey: string, orgId: string, name: string, opts?: CreateEndpointOptions): Promise<WebhookEndpoint>;
31
+ export declare function updateEndpoint(apiKey: string, orgId: string, endpointId: string, payload: UpdateEndpointPayload): Promise<WebhookEndpoint>;
24
32
  export declare function listEndpoints(apiKey: string, orgId: string): Promise<WebhookEndpoint[]>;
25
33
  export declare function deleteEndpoint(apiKey: string, orgId: string, endpointId: string): Promise<void>;
26
34
  export declare function getDelivery(apiKey: string, orgId: string, deliveryId: string): Promise<Delivery>;
package/dist/webhook.js CHANGED
@@ -2,6 +2,7 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.EXPOSES = void 0;
4
4
  exports.createEndpoint = createEndpoint;
5
+ exports.updateEndpoint = updateEndpoint;
5
6
  exports.listEndpoints = listEndpoints;
6
7
  exports.deleteEndpoint = deleteEndpoint;
7
8
  exports.getDelivery = getDelivery;
@@ -10,6 +11,7 @@ const config_1 = require("./config");
10
11
  exports.EXPOSES = [
11
12
  'POST /webhook/orgs/{org_id}/endpoints',
12
13
  'GET /webhook/orgs/{org_id}/endpoints',
14
+ 'PATCH /webhook/orgs/{org_id}/endpoints/{endpoint_id}',
13
15
  'DELETE /webhook/orgs/{org_id}/endpoints/{endpoint_id}',
14
16
  'GET /webhook/orgs/{org_id}/deliveries/{delivery_id}',
15
17
  ];
@@ -19,8 +21,13 @@ async function createEndpoint(apiKey, orgId, name, opts = {}) {
19
21
  body.description = opts.description;
20
22
  if (opts.crm_email_path !== undefined)
21
23
  body.crm_email_path = opts.crm_email_path;
24
+ if (opts.forward_url !== undefined)
25
+ body.forward_url = opts.forward_url;
22
26
  return (0, client_1.request)('POST', `${config_1.WEBHOOK_BASE}/webhook/orgs/${encodeURIComponent(orgId)}/endpoints`, apiKey, body);
23
27
  }
28
+ async function updateEndpoint(apiKey, orgId, endpointId, payload) {
29
+ return (0, client_1.request)('PATCH', `${config_1.WEBHOOK_BASE}/webhook/orgs/${encodeURIComponent(orgId)}/endpoints/${encodeURIComponent(endpointId)}`, apiKey, payload);
30
+ }
24
31
  async function listEndpoints(apiKey, orgId) {
25
32
  return (0, client_1.request)('GET', `${config_1.WEBHOOK_BASE}/webhook/orgs/${encodeURIComponent(orgId)}/endpoints`, apiKey);
26
33
  }
@@ -22,6 +22,12 @@ export type WorkflowStep = {
22
22
  type: 'slack_message';
23
23
  webhook_url: string;
24
24
  text: string;
25
+ } | {
26
+ type: 'http_request' | 'http';
27
+ url: string;
28
+ method?: 'GET' | 'POST' | 'PATCH' | 'PUT' | 'DELETE';
29
+ body?: string;
30
+ headers?: Record<string, string>;
25
31
  };
26
32
  export interface Workflow {
27
33
  id: string;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@myapihq/sdk",
3
3
  "license": "Apache-2.0",
4
- "version": "1.2.2",
4
+ "version": "1.2.5",
5
5
  "description": "TypeScript SDK for the MyAPI ecosystem",
6
6
  "main": "dist/index.js",
7
7
  "types": "dist/index.d.ts",
package/src/client.ts CHANGED
@@ -1,7 +1,10 @@
1
1
  import { ApiResponse } from './types';
2
2
 
3
3
  export class MyApiError extends Error {
4
- constructor(public code: string, public status: number, public detail?: string) {
4
+ // `body` is the full error object as returned by the backend — preserved so
5
+ // callers can read service-specific fields the SDK doesn't otherwise model
6
+ // (e.g. CF_API_ERROR carries `cf_message` / `cf_status`).
7
+ constructor(public code: string, public status: number, public detail?: string, public body?: Record<string, unknown>) {
5
8
  // Surface the detail in the message so console.error / err.message
6
9
  // shows useful info without callers having to look at err.detail.
7
10
  super(detail ? `${code} (${status}): ${detail}` : code);
@@ -69,7 +72,8 @@ export async function request<T>(
69
72
  const err = result?.error;
70
73
  const code = typeof err === 'object' ? (err?.code || 'unknown_error') : (err || 'unknown_error');
71
74
  const detail = typeof err === 'object' ? (err?.message || undefined) : undefined;
72
- throw new MyApiError(code, response.status, detail);
75
+ const body = typeof err === 'object' ? err : undefined;
76
+ throw new MyApiError(code, response.status, detail, body);
73
77
  }
74
78
 
75
79
  const apiResponse = result as ApiResponse<T>;
@@ -77,7 +81,8 @@ export async function request<T>(
77
81
  const err = apiResponse.error as any;
78
82
  const code = typeof err === 'object' ? (err?.code || 'unknown_error') : (err || 'unknown_error');
79
83
  const detail = typeof err === 'object' ? (err?.message || undefined) : undefined;
80
- throw new MyApiError(code, response.status, detail);
84
+ const body = typeof err === 'object' ? err : undefined;
85
+ throw new MyApiError(code, response.status, detail, body);
81
86
  }
82
87
 
83
88
  return apiResponse.data as T;
package/src/config.ts CHANGED
@@ -12,6 +12,9 @@ const GATEWAY = 'https://api.myapihq.com';
12
12
  export const HQ_BASE = process.env.MYAPI_HQ_URL ?? process.env.MYAPI_API_BASE ?? GATEWAY;
13
13
  export const DOMAIN_BASE = process.env.MYAPI_DOMAIN_URL ?? 'https://api.mydomainapi.com';
14
14
  export const FUNNEL_BASE = process.env.MYAPI_FUNNEL_URL ?? 'https://api.myfunnelapi.com';
15
+ // New slots — routed via the gateway until brand hosts are DNS-wired
16
+ // (mirrors the LLM/DATABASE/CRM pattern below).
17
+ export const FUNCTION_BASE= process.env.MYAPI_FUNCTION_URL?? GATEWAY;
15
18
  export const IMAGE_BASE = process.env.MYAPI_IMAGE_URL ?? 'https://api.myimageapi.com';
16
19
  export const WEBHOOK_BASE = process.env.MYAPI_WEBHOOK_URL ?? 'https://api.mywebhookapi.com';
17
20
  export const WORKFLOW_BASE= process.env.MYAPI_WORKFLOW_URL?? 'https://api.myworkflowapi.com';
package/src/domain.ts CHANGED
@@ -17,6 +17,8 @@ export const EXPOSES: Exposes = [
17
17
  'POST /domain/orgs/{org_id}/{domain}/records',
18
18
  'PATCH /domain/orgs/{org_id}/{domain}/records/{record_id}',
19
19
  'DELETE /domain/orgs/{org_id}/{domain}/records/{record_id}',
20
+ 'POST /domain/orgs/{org_id}/{domain}/email-infra',
21
+ 'POST /domain/orgs/{org_id}/{domain}/retry-provisioning',
20
22
  ];
21
23
 
22
24
 
@@ -27,6 +29,19 @@ export interface DomainRecord {
27
29
  expires_at?: string;
28
30
  days_until_expiry?: number;
29
31
  email_infra_ready?: boolean;
32
+ // New status surface (post-2026-05-14 backend). Email infra is opt-in and
33
+ // always lives on a subdomain — apex is never touched.
34
+ email_infra?: 'skipped' | 'pending' | 'ready' | 'error';
35
+ email_subdomain?: string; // Only set when email_infra ∈ {pending, ready, error}.
36
+ dns_active?: boolean;
37
+ steps_completed?: string[];
38
+ error_detail?: {
39
+ failed_step: string;
40
+ message: string;
41
+ retryable: boolean;
42
+ attempt_count?: number;
43
+ last_attempt_at?: string;
44
+ };
30
45
  org_id?: string | null;
31
46
  created_at: string;
32
47
  }
@@ -110,11 +125,29 @@ export async function getDomainStatus(apiKey: string, orgId: string, domain: str
110
125
  return request('GET', `${BASE_URL}/domain/orgs/${encodeURIComponent(orgId)}/${encodeURIComponent(domain)}/status`, apiKey);
111
126
  }
112
127
 
113
- export async function assignDomain(apiKey: string, orgId: string, domain: string): Promise<DomainRecord> {
114
- return request('POST', `${BASE_URL}/domain/orgs/${encodeURIComponent(orgId)}/${encodeURIComponent(domain)}/assign`, apiKey, { org_id: orgId });
128
+ // Assign returns the bound Worker routes so the CLI can show users what's
129
+ // live without hardcoding "apex + www" (which would skew if backend changes
130
+ // the default convention). `include_www` defaults to true at the backend.
131
+ export interface AssignDomainResponse {
132
+ domain: string;
133
+ org_id: string | null;
134
+ include_www: boolean;
135
+ routes_bound: string[];
136
+ }
137
+
138
+ export async function assignDomain(apiKey: string, orgId: string, domain: string, opts?: { includeWww?: boolean; funnelId?: string }): Promise<AssignDomainResponse> {
139
+ const body: Record<string, unknown> = { org_id: orgId };
140
+ if (opts?.includeWww === false) body.include_www = false;
141
+ // Backend (2026-05-15): optional funnel_id picks which funnel the domain
142
+ // is bound to. Defaults server-side to the org's only funnel (or first
143
+ // when ambiguous, today; will become required once N-funnels-per-org lands
144
+ // for real). Pre-emptively threading it through gives agents a way to be
145
+ // explicit when the migration completes without an SDK shape change.
146
+ if (opts?.funnelId) body.funnel_id = opts.funnelId;
147
+ return request('POST', `${BASE_URL}/domain/orgs/${encodeURIComponent(orgId)}/${encodeURIComponent(domain)}/assign`, apiKey, body);
115
148
  }
116
149
 
117
- export async function unassignDomain(apiKey: string, orgId: string, domain: string): Promise<DomainRecord> {
150
+ export async function unassignDomain(apiKey: string, orgId: string, domain: string): Promise<AssignDomainResponse> {
118
151
  return request('POST', `${BASE_URL}/domain/orgs/${encodeURIComponent(orgId)}/${encodeURIComponent(domain)}/assign`, apiKey, { org_id: null });
119
152
  }
120
153
 
@@ -172,6 +205,34 @@ export async function deleteDnsRecord(apiKey: string, orgId: string, domain: str
172
205
  return request('DELETE', `${BASE_URL}/domain/orgs/${encodeURIComponent(orgId)}/${encodeURIComponent(domain)}/records/${encodeURIComponent(recordId)}`, apiKey);
173
206
  }
174
207
 
208
+ // Opt in to MyAPI-managed email on a subdomain. Apex is never touched; the
209
+ // backend provisions an SES identity on <subdomain>.<domain> and writes
210
+ // DKIM/SPF/DMARC records there. Default subdomain is `mail`.
211
+ export interface EmailInfraResponse {
212
+ domain: string;
213
+ email_infra: 'pending' | 'ready' | 'error';
214
+ email_subdomain: string;
215
+ next_step?: string;
216
+ }
217
+
218
+ export async function setupEmailInfra(apiKey: string, orgId: string, domain: string, subdomain?: string): Promise<EmailInfraResponse> {
219
+ const body = subdomain ? { subdomain } : {};
220
+ return request('POST', `${BASE_URL}/domain/orgs/${encodeURIComponent(orgId)}/${encodeURIComponent(domain)}/email-infra`, apiKey, body);
221
+ }
222
+
223
+ // Re-run provisioning from the failed step when status=infra_error and the
224
+ // error is retryable. Returns a transient `{ email_infra: 'pending', next_step }`
225
+ // shape; caller polls `getDomainStatus` until status flips off `infra_error`.
226
+ export interface RetryProvisioningResponse {
227
+ domain: string;
228
+ email_infra?: string;
229
+ next_step?: string;
230
+ }
231
+
232
+ export async function retryProvisioning(apiKey: string, orgId: string, domain: string): Promise<RetryProvisioningResponse> {
233
+ return request('POST', `${BASE_URL}/domain/orgs/${encodeURIComponent(orgId)}/${encodeURIComponent(domain)}/retry-provisioning`, apiKey);
234
+ }
235
+
175
236
  export async function updateDomainSettings(apiKey: string, orgId: string, domain: string, payload: {
176
237
  security_level?: 'essentially_off' | 'medium' | 'high' | 'under_attack';
177
238
  browser_check?: 'on' | 'off';
@@ -0,0 +1,63 @@
1
+ import { request } from './client';
2
+ import { FUNCTION_BASE as BASE_URL } from './config';
3
+ import type { Exposes } from './exposes';
4
+
5
+ // Backend: Story 1 (per myapi-hq/internal/routes/function/crud.go). Metadata
6
+ // + scoped API key issuance only — Cloudflare Worker upload, /logs, and /env
7
+ // land in Story 2-5. The SDK surface stays minimal until those endpoints
8
+ // exist; do not add forward-compat fields here that the backend will silently
9
+ // ignore.
10
+ export const EXPOSES: Exposes = [
11
+ 'POST /function/orgs/{org_id}/functions',
12
+ 'GET /function/orgs/{org_id}/functions',
13
+ 'GET /function/orgs/{org_id}/functions/{id}',
14
+ 'DELETE /function/orgs/{org_id}/functions/{id}',
15
+ ];
16
+
17
+ export type TriggerType = 'http' | 'cron';
18
+
19
+ // Mirrors the backend's `Function` struct in crud.go. `invocation_url`
20
+ // stays an empty string in Story 1; populated by Story 2 once the CF Worker
21
+ // upload pipeline lands.
22
+ export interface Fn {
23
+ id: string;
24
+ org_id: string;
25
+ name: string;
26
+ trigger_type: TriggerType;
27
+ cron_schedule?: string;
28
+ invocation_url: string;
29
+ created_at: string;
30
+ updated_at: string;
31
+ }
32
+
33
+ export interface CreatePayload {
34
+ name: string; // required; ^[a-z0-9][a-z0-9-]{0,49}$
35
+ trigger_type?: TriggerType; // optional, defaults to 'http' server-side
36
+ cron_schedule?: string; // required if trigger_type === 'cron'
37
+ }
38
+
39
+ // POST response envelope includes the scoped API key — returned ONCE. The
40
+ // caller must persist it if they want to use it directly; the SDK does not
41
+ // store it. Story 2+ will use this key internally inside the function's
42
+ // runtime shim (`globalThis.MYAPI`).
43
+ export interface CreateResponse {
44
+ function: Fn;
45
+ scoped_api_key: string;
46
+ scoped_api_key_id: string;
47
+ }
48
+
49
+ export async function createFunction(apiKey: string, orgId: string, payload: CreatePayload): Promise<CreateResponse> {
50
+ return request('POST', `${BASE_URL}/function/orgs/${encodeURIComponent(orgId)}/functions`, apiKey, payload);
51
+ }
52
+
53
+ export async function listFunctions(apiKey: string, orgId: string): Promise<Fn[]> {
54
+ return request('GET', `${BASE_URL}/function/orgs/${encodeURIComponent(orgId)}/functions`, apiKey);
55
+ }
56
+
57
+ export async function getFunction(apiKey: string, orgId: string, fnId: string): Promise<Fn> {
58
+ return request('GET', `${BASE_URL}/function/orgs/${encodeURIComponent(orgId)}/functions/${encodeURIComponent(fnId)}`, apiKey);
59
+ }
60
+
61
+ export async function deleteFunction(apiKey: string, orgId: string, fnId: string): Promise<void> {
62
+ return request('DELETE', `${BASE_URL}/function/orgs/${encodeURIComponent(orgId)}/functions/${encodeURIComponent(fnId)}`, apiKey);
63
+ }
package/src/funnel.ts CHANGED
@@ -12,9 +12,16 @@ export const EXPOSES: Exposes = [
12
12
  'GET /funnel/orgs/{org_id}/funnels/{funnel_id}/pages',
13
13
  ];
14
14
 
15
-
16
-
17
- export interface Funnel { id: string; org_id: string; created_at: string; updated_at: string }
15
+ export interface Funnel {
16
+ id: string;
17
+ org_id: string;
18
+ // Backend (2026-05-15): POST /funnels now accepts optional `name`. Defaults
19
+ // to the org's preview_subdomain for back-compat. Get / list responses
20
+ // include it when set.
21
+ name?: string;
22
+ created_at: string;
23
+ updated_at: string;
24
+ }
18
25
  export interface CreateFunnelResponse { funnel: Funnel; subdomain_url?: string; domain_url?: string }
19
26
  export interface VerifyResult {
20
27
  pages?: Array<{ slug: string; tests: VerifyTest[] }>;
@@ -22,8 +29,10 @@ export interface VerifyResult {
22
29
  }
23
30
  export interface VerifyTest { type: 'link' | 'webhook'; url: string; passed: boolean; details: string }
24
31
 
25
- export async function createFunnel(apiKey: string, orgId: string): Promise<CreateFunnelResponse> {
26
- return request('POST', `${BASE_URL}/funnel/orgs/${encodeURIComponent(orgId)}/funnels`, apiKey, {});
32
+ export async function createFunnel(apiKey: string, orgId: string, opts?: { name?: string }): Promise<CreateFunnelResponse> {
33
+ // v2: accept optional name for N-funnels-per-org. Backend rejects names
34
+ // matching the reserved-suffix rules; agent should pre-validate.
35
+ return request('POST', `${BASE_URL}/funnel/orgs/${encodeURIComponent(orgId)}/funnels`, apiKey, opts?.name ? { name: opts.name } : {});
27
36
  }
28
37
  export async function getFunnel(apiKey: string, orgId: string, funnelId: string): Promise<Funnel> {
29
38
  return request('GET', `${BASE_URL}/funnel/orgs/${encodeURIComponent(orgId)}/funnels/${encodeURIComponent(funnelId)}`, apiKey);
package/src/index.ts CHANGED
@@ -22,3 +22,4 @@ export * as audience from './audience';
22
22
  export * as llm from './llm';
23
23
  export * as database from './database';
24
24
  export * as crm from './crm';
25
+ export * as fn from './function';
package/src/services.ts CHANGED
@@ -217,4 +217,21 @@ export const SERVICES: readonly ServiceMeta[] = [
217
217
  status: 'ga',
218
218
  keywords: k('llm', 'completion', 'embedding', 'gemini'),
219
219
  },
220
+ {
221
+ // CLI top-level command is `fn`; SDK namespace is `fn`; skill directory is
222
+ // `my-function-api` (the agent-discoverable brand name).
223
+ //
224
+ // Backend status: Story 1 shipped (metadata + scoped API key issuance).
225
+ // Story 2 (CF Workers upload, invocation URL), Story 4 (/logs), Story 5
226
+ // (/env) pending. 'planned' reflects "partial backend, not stable GA."
227
+ module: 'fn',
228
+ skill: 'my-function-api',
229
+ domain: 'myfunctionapi.com',
230
+ description: 'Run JavaScript functions on the edge. HTTP or cron triggers. Pre-injected MYAPI SDK scoped to the org — no auth tokens in user code.',
231
+ category: 'compute',
232
+ status: 'planned',
233
+ keywords: k('function', 'serverless', 'edge', 'cloudflare-workers', 'http-handler', 'cron'),
234
+ },
235
+ // Note: my-payments-api removed from registry — backend has no /payments/*
236
+ // endpoints yet. Re-add when backend ships per tech-spec-my-payments-api-2026-05-14.md.
220
237
  ] as const;
package/src/webhook.ts CHANGED
@@ -5,6 +5,7 @@ import type { Exposes } from './exposes';
5
5
  export const EXPOSES: Exposes = [
6
6
  'POST /webhook/orgs/{org_id}/endpoints',
7
7
  'GET /webhook/orgs/{org_id}/endpoints',
8
+ 'PATCH /webhook/orgs/{org_id}/endpoints/{endpoint_id}',
8
9
  'DELETE /webhook/orgs/{org_id}/endpoints/{endpoint_id}',
9
10
  'GET /webhook/orgs/{org_id}/deliveries/{delivery_id}',
10
11
  ];
@@ -36,15 +37,34 @@ export interface CreateEndpointOptions {
36
37
  // 'email' (top-level). For nested shapes pass the dot-path (e.g.
37
38
  // 'data.object.customer_email' for Stripe, 'sender.email' for GitHub).
38
39
  crm_email_path?: string;
40
+ // Backend (2026-05-15): optional URL to forward every inbound delivery to
41
+ // (POST, async, best-effort). Headers `X-MyAPI-Webhook-Endpoint-Id` and
42
+ // `X-MyAPI-Webhook-Delivery-Id` are attached. Use http:// or https://.
43
+ // Empty string disables forwarding.
44
+ forward_url?: string;
45
+ }
46
+
47
+ // All fields optional — only those passed are mutated. Mirrors the backend
48
+ // PATCH /webhook/orgs/{org_id}/endpoints/{id} contract (slug is immutable).
49
+ export interface UpdateEndpointPayload {
50
+ name?: string;
51
+ description?: string;
52
+ crm_email_path?: string;
53
+ forward_url?: string;
39
54
  }
40
55
 
41
56
  export async function createEndpoint(apiKey: string, orgId: string, name: string, opts: CreateEndpointOptions = {}): Promise<WebhookEndpoint> {
42
57
  const body: Record<string, unknown> = { name };
43
58
  if (opts.description !== undefined) body.description = opts.description;
44
59
  if (opts.crm_email_path !== undefined) body.crm_email_path = opts.crm_email_path;
60
+ if (opts.forward_url !== undefined) body.forward_url = opts.forward_url;
45
61
  return request('POST', `${BASE_URL}/webhook/orgs/${encodeURIComponent(orgId)}/endpoints`, apiKey, body);
46
62
  }
47
63
 
64
+ export async function updateEndpoint(apiKey: string, orgId: string, endpointId: string, payload: UpdateEndpointPayload): Promise<WebhookEndpoint> {
65
+ return request('PATCH', `${BASE_URL}/webhook/orgs/${encodeURIComponent(orgId)}/endpoints/${encodeURIComponent(endpointId)}`, apiKey, payload);
66
+ }
67
+
48
68
  export async function listEndpoints(apiKey: string, orgId: string): Promise<WebhookEndpoint[]> {
49
69
  return request('GET', `${BASE_URL}/webhook/orgs/${encodeURIComponent(orgId)}/endpoints`, apiKey);
50
70
  }
package/src/workflow.ts CHANGED
@@ -29,7 +29,11 @@ export const EXPOSES: Exposes = [
29
29
  */
30
30
  export type WorkflowStep =
31
31
  | { type: 'send_email'; from: string; to: string; subject: string; template_id?: string; html?: string }
32
- | { type: 'slack_message'; webhook_url: string; text: string };
32
+ | { type: 'slack_message'; webhook_url: string; text: string }
33
+ // Backend (2026-05-15): `http_request` step is now accepted (alias: `http`).
34
+ // POSTs (or other method) to `url` with the inbound payload; supports
35
+ // template substitution in url/body the same way email steps do.
36
+ | { type: 'http_request' | 'http'; url: string; method?: 'GET' | 'POST' | 'PATCH' | 'PUT' | 'DELETE'; body?: string; headers?: Record<string, string> };
33
37
 
34
38
  export interface Workflow {
35
39
  id: string;