@myapihq/sdk 1.2.4 → 1.2.6
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/config.d.ts +2 -0
- package/dist/config.js +5 -1
- package/dist/domain.d.ts +11 -2
- package/dist/domain.js +12 -2
- package/dist/function.d.ts +43 -0
- package/dist/function.js +69 -0
- package/dist/funnel.d.ts +22 -1
- package/dist/funnel.js +36 -2
- package/dist/hq.d.ts +33 -12
- package/dist/hq.js +38 -3
- package/dist/index.d.ts +2 -0
- package/dist/index.js +3 -1
- package/dist/payments.d.ts +44 -0
- package/dist/payments.js +54 -0
- package/dist/services.js +31 -0
- package/dist/webhook.d.ts +8 -0
- package/dist/webhook.js +7 -0
- package/dist/workflow.d.ts +6 -0
- package/package.json +1 -1
- package/src/config.ts +4 -0
- package/src/domain.ts +21 -3
- package/src/function.ts +129 -0
- package/src/funnel.ts +80 -6
- package/src/hq.ts +78 -3
- package/src/index.ts +2 -0
- package/src/payments.ts +97 -0
- package/src/services.ts +31 -0
- package/src/webhook.ts +20 -0
- package/src/workflow.ts +5 -1
package/dist/config.d.ts
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
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;
|
|
5
|
+
export declare const PAYMENTS_BASE: string;
|
|
4
6
|
export declare const IMAGE_BASE: string;
|
|
5
7
|
export declare const WEBHOOK_BASE: string;
|
|
6
8
|
export declare const WORKFLOW_BASE: string;
|
package/dist/config.js
CHANGED
|
@@ -8,11 +8,15 @@
|
|
|
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.PAYMENTS_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;
|
|
19
|
+
exports.PAYMENTS_BASE = process.env.MYAPI_PAYMENTS_URL ?? GATEWAY;
|
|
16
20
|
exports.IMAGE_BASE = process.env.MYAPI_IMAGE_URL ?? 'https://api.myimageapi.com';
|
|
17
21
|
exports.WEBHOOK_BASE = process.env.MYAPI_WEBHOOK_URL ?? 'https://api.mywebhookapi.com';
|
|
18
22
|
exports.WORKFLOW_BASE = process.env.MYAPI_WORKFLOW_URL ?? 'https://api.myworkflowapi.com';
|
package/dist/domain.d.ts
CHANGED
|
@@ -67,8 +67,17 @@ export interface ImportDomainResponse {
|
|
|
67
67
|
export declare function importDomain(apiKey: string, orgId: string, domain: string): Promise<ImportDomainResponse>;
|
|
68
68
|
export declare function listDomains(apiKey: string, orgId: string, filter?: string): Promise<DomainRecord[]>;
|
|
69
69
|
export declare function getDomainStatus(apiKey: string, orgId: string, domain: string): Promise<DomainRecord>;
|
|
70
|
-
export
|
|
71
|
-
|
|
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>;
|
|
72
81
|
export declare function getDomainSettings(apiKey: string, orgId: string, domain: string): Promise<DomainSettings>;
|
|
73
82
|
export type DnsRecordType = 'A' | 'AAAA' | 'CNAME' | 'MX' | 'TXT';
|
|
74
83
|
export interface DnsRecord {
|
package/dist/domain.js
CHANGED
|
@@ -61,8 +61,18 @@ async function listDomains(apiKey, orgId, filter) {
|
|
|
61
61
|
async function getDomainStatus(apiKey, orgId, domain) {
|
|
62
62
|
return (0, client_1.request)('GET', `${config_1.DOMAIN_BASE}/domain/orgs/${encodeURIComponent(orgId)}/${encodeURIComponent(domain)}/status`, apiKey);
|
|
63
63
|
}
|
|
64
|
-
async function assignDomain(apiKey, orgId, domain) {
|
|
65
|
-
|
|
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);
|
|
66
76
|
}
|
|
67
77
|
async function unassignDomain(apiKey, orgId, domain) {
|
|
68
78
|
return (0, client_1.request)('POST', `${config_1.DOMAIN_BASE}/domain/orgs/${encodeURIComponent(orgId)}/${encodeURIComponent(domain)}/assign`, apiKey, { org_id: null });
|
|
@@ -0,0 +1,43 @@
|
|
|
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 interface DeployResponse {
|
|
25
|
+
function: Fn;
|
|
26
|
+
invocation_url: string;
|
|
27
|
+
scoped_api_key: string;
|
|
28
|
+
}
|
|
29
|
+
export interface FunctionRun {
|
|
30
|
+
id: string;
|
|
31
|
+
function_id: string;
|
|
32
|
+
invoked_at: string;
|
|
33
|
+
duration_ms?: number;
|
|
34
|
+
status?: string;
|
|
35
|
+
error_message?: string;
|
|
36
|
+
}
|
|
37
|
+
export declare function createFunction(apiKey: string, orgId: string, payload: CreatePayload): Promise<CreateResponse>;
|
|
38
|
+
export declare function listFunctions(apiKey: string, orgId: string): Promise<Fn[]>;
|
|
39
|
+
export declare function getFunction(apiKey: string, orgId: string, fnId: string): Promise<Fn>;
|
|
40
|
+
export declare function deleteFunction(apiKey: string, orgId: string, fnId: string): Promise<void>;
|
|
41
|
+
export declare function uploadBundle(apiKey: string, orgId: string, fnId: string, bundle: Blob | Buffer | string, filename?: string): Promise<DeployResponse>;
|
|
42
|
+
export declare function setFunctionEnv(apiKey: string, orgId: string, fnId: string, name: string, value: string): Promise<void>;
|
|
43
|
+
export declare function listFunctionRuns(apiKey: string, orgId: string, fnId: string): Promise<FunctionRun[]>;
|
package/dist/function.js
ADDED
|
@@ -0,0 +1,69 @@
|
|
|
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
|
+
exports.uploadBundle = uploadBundle;
|
|
9
|
+
exports.setFunctionEnv = setFunctionEnv;
|
|
10
|
+
exports.listFunctionRuns = listFunctionRuns;
|
|
11
|
+
const client_1 = require("./client");
|
|
12
|
+
const config_1 = require("./config");
|
|
13
|
+
// Backend: Story 1 (metadata + scoped API key) and Story 2/4/5 (deploy,
|
|
14
|
+
// runs, env) per myapi-hq/internal/routes/function/. The /logs endpoint is
|
|
15
|
+
// still pending — do not add forward-compat fields the backend will ignore.
|
|
16
|
+
exports.EXPOSES = [
|
|
17
|
+
'POST /function/orgs/{org_id}/functions',
|
|
18
|
+
'GET /function/orgs/{org_id}/functions',
|
|
19
|
+
'GET /function/orgs/{org_id}/functions/{id}',
|
|
20
|
+
'DELETE /function/orgs/{org_id}/functions/{id}',
|
|
21
|
+
'POST /function/orgs/{org_id}/functions/{id}/bundle',
|
|
22
|
+
'POST /function/orgs/{org_id}/functions/{id}/env',
|
|
23
|
+
'GET /function/orgs/{org_id}/functions/{id}/runs',
|
|
24
|
+
];
|
|
25
|
+
async function createFunction(apiKey, orgId, payload) {
|
|
26
|
+
return (0, client_1.request)('POST', `${config_1.FUNCTION_BASE}/function/orgs/${encodeURIComponent(orgId)}/functions`, apiKey, payload);
|
|
27
|
+
}
|
|
28
|
+
async function listFunctions(apiKey, orgId) {
|
|
29
|
+
return (0, client_1.request)('GET', `${config_1.FUNCTION_BASE}/function/orgs/${encodeURIComponent(orgId)}/functions`, apiKey);
|
|
30
|
+
}
|
|
31
|
+
async function getFunction(apiKey, orgId, fnId) {
|
|
32
|
+
return (0, client_1.request)('GET', `${config_1.FUNCTION_BASE}/function/orgs/${encodeURIComponent(orgId)}/functions/${encodeURIComponent(fnId)}`, apiKey);
|
|
33
|
+
}
|
|
34
|
+
async function deleteFunction(apiKey, orgId, fnId) {
|
|
35
|
+
return (0, client_1.request)('DELETE', `${config_1.FUNCTION_BASE}/function/orgs/${encodeURIComponent(orgId)}/functions/${encodeURIComponent(fnId)}`, apiKey);
|
|
36
|
+
}
|
|
37
|
+
// uploadBundle deploys a single-file JS bundle (Story 2). The backend
|
|
38
|
+
// wraps it with the MYAPI shim and uploads to Cloudflare Workers. The
|
|
39
|
+
// multipart field MUST be named `bundle`. Raw upload cap is 4MB.
|
|
40
|
+
async function uploadBundle(apiKey, orgId, fnId, bundle, filename = 'bundle.js') {
|
|
41
|
+
const formData = new FormData();
|
|
42
|
+
formData.append('bundle', new Blob([bundle], { type: 'application/javascript' }), filename);
|
|
43
|
+
const response = await fetch(`${config_1.FUNCTION_BASE}/function/orgs/${encodeURIComponent(orgId)}/functions/${encodeURIComponent(fnId)}/bundle`, { method: 'POST', headers: { Authorization: `Bearer ${apiKey}` }, body: formData });
|
|
44
|
+
let result;
|
|
45
|
+
try {
|
|
46
|
+
result = await response.json();
|
|
47
|
+
}
|
|
48
|
+
catch {
|
|
49
|
+
throw new client_1.MyApiError('invalid_json_response', response.status);
|
|
50
|
+
}
|
|
51
|
+
if (!response.ok || !result?.success) {
|
|
52
|
+
const err = result?.error;
|
|
53
|
+
const code = typeof err === 'object' ? (err?.code || 'unknown_error') : (err || 'unknown_error');
|
|
54
|
+
const detail = typeof err === 'object' ? err?.message : undefined;
|
|
55
|
+
throw new client_1.MyApiError(code, response.status, detail, typeof err === 'object' ? err : undefined);
|
|
56
|
+
}
|
|
57
|
+
return result.data;
|
|
58
|
+
}
|
|
59
|
+
// setFunctionEnv writes a secret (Stripe key, etc.) as a Cloudflare Worker
|
|
60
|
+
// Secret on the deployed script (Story 5). The function must already be
|
|
61
|
+
// deployed. The value is never stored in MyAPI Postgres or returned.
|
|
62
|
+
async function setFunctionEnv(apiKey, orgId, fnId, name, value) {
|
|
63
|
+
return (0, client_1.request)('POST', `${config_1.FUNCTION_BASE}/function/orgs/${encodeURIComponent(orgId)}/functions/${encodeURIComponent(fnId)}/env`, apiKey, { name, value });
|
|
64
|
+
}
|
|
65
|
+
// listFunctionRuns returns recent invocation records, most recent first
|
|
66
|
+
// (Story 4). Capped at 100 server-side.
|
|
67
|
+
async function listFunctionRuns(apiKey, orgId, fnId) {
|
|
68
|
+
return (0, client_1.request)('GET', `${config_1.FUNCTION_BASE}/function/orgs/${encodeURIComponent(orgId)}/functions/${encodeURIComponent(fnId)}/runs`, apiKey);
|
|
69
|
+
}
|
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
|
|
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>;
|
|
@@ -46,3 +49,21 @@ export declare function verifyFunnel(apiKey: string, orgId: string, funnelId: st
|
|
|
46
49
|
html?: string;
|
|
47
50
|
slug?: string;
|
|
48
51
|
}): Promise<VerifyResult>;
|
|
52
|
+
export interface PublishFile {
|
|
53
|
+
path: string;
|
|
54
|
+
content: Blob | Buffer | string;
|
|
55
|
+
}
|
|
56
|
+
export interface PublishOptions {
|
|
57
|
+
env?: 'dev' | 'prod';
|
|
58
|
+
spaMode?: boolean;
|
|
59
|
+
apiFunctionId?: string;
|
|
60
|
+
}
|
|
61
|
+
export interface PublishResult {
|
|
62
|
+
manifest_id: string;
|
|
63
|
+
channel: 'dev' | 'prod';
|
|
64
|
+
file_count: number;
|
|
65
|
+
size_bytes: number;
|
|
66
|
+
spa_mode: boolean;
|
|
67
|
+
published_url: string;
|
|
68
|
+
}
|
|
69
|
+
export declare function publishFiles(apiKey: string, orgId: string, funnelId: string, files: PublishFile[], opts?: PublishOptions): Promise<PublishResult>;
|
package/dist/funnel.js
CHANGED
|
@@ -8,6 +8,7 @@ exports.deleteFunnel = deleteFunnel;
|
|
|
8
8
|
exports.pushFunnelPage = pushFunnelPage;
|
|
9
9
|
exports.listFunnelPages = listFunnelPages;
|
|
10
10
|
exports.verifyFunnel = verifyFunnel;
|
|
11
|
+
exports.publishFiles = publishFiles;
|
|
11
12
|
const client_1 = require("./client");
|
|
12
13
|
const config_1 = require("./config");
|
|
13
14
|
exports.EXPOSES = [
|
|
@@ -18,9 +19,12 @@ exports.EXPOSES = [
|
|
|
18
19
|
'POST /funnel/orgs/{org_id}/funnels/{funnel_id}/push-page',
|
|
19
20
|
'POST /funnel/orgs/{org_id}/funnels/{funnel_id}/verify',
|
|
20
21
|
'GET /funnel/orgs/{org_id}/funnels/{funnel_id}/pages',
|
|
22
|
+
'POST /funnel/orgs/{org_id}/funnels/{funnel_id}/files',
|
|
21
23
|
];
|
|
22
|
-
async function createFunnel(apiKey, orgId) {
|
|
23
|
-
|
|
24
|
+
async function createFunnel(apiKey, orgId, opts) {
|
|
25
|
+
// v2: accept optional name for N-funnels-per-org. Backend rejects names
|
|
26
|
+
// matching the reserved-suffix rules; agent should pre-validate.
|
|
27
|
+
return (0, client_1.request)('POST', `${config_1.FUNNEL_BASE}/funnel/orgs/${encodeURIComponent(orgId)}/funnels`, apiKey, opts?.name ? { name: opts.name } : {});
|
|
24
28
|
}
|
|
25
29
|
async function getFunnel(apiKey, orgId, funnelId) {
|
|
26
30
|
return (0, client_1.request)('GET', `${config_1.FUNNEL_BASE}/funnel/orgs/${encodeURIComponent(orgId)}/funnels/${encodeURIComponent(funnelId)}`, apiKey);
|
|
@@ -46,3 +50,33 @@ async function listFunnelPages(apiKey, orgId, funnelId) {
|
|
|
46
50
|
async function verifyFunnel(apiKey, orgId, funnelId, opts) {
|
|
47
51
|
return (0, client_1.request)('POST', `${config_1.FUNNEL_BASE}/funnel/orgs/${encodeURIComponent(orgId)}/funnels/${encodeURIComponent(funnelId)}/verify`, apiKey, opts || {});
|
|
48
52
|
}
|
|
53
|
+
// publishFiles is the my-funnel-api v2 directory publish. Every file is a
|
|
54
|
+
// multipart part named `files`; the part's filename carries the site path.
|
|
55
|
+
// 25MB total cap server-side.
|
|
56
|
+
async function publishFiles(apiKey, orgId, funnelId, files, opts = {}) {
|
|
57
|
+
const formData = new FormData();
|
|
58
|
+
for (const f of files) {
|
|
59
|
+
formData.append('files', new Blob([f.content]), f.path);
|
|
60
|
+
}
|
|
61
|
+
if (opts.env)
|
|
62
|
+
formData.append('env', opts.env);
|
|
63
|
+
if (opts.spaMode !== undefined)
|
|
64
|
+
formData.append('spa_mode', String(opts.spaMode));
|
|
65
|
+
if (opts.apiFunctionId)
|
|
66
|
+
formData.append('api_function_id', opts.apiFunctionId);
|
|
67
|
+
const response = await fetch(`${config_1.FUNNEL_BASE}/funnel/orgs/${encodeURIComponent(orgId)}/funnels/${encodeURIComponent(funnelId)}/files`, { method: 'POST', headers: { Authorization: `Bearer ${apiKey}` }, body: formData });
|
|
68
|
+
let result;
|
|
69
|
+
try {
|
|
70
|
+
result = await response.json();
|
|
71
|
+
}
|
|
72
|
+
catch {
|
|
73
|
+
throw new client_1.MyApiError('invalid_json_response', response.status);
|
|
74
|
+
}
|
|
75
|
+
if (!response.ok || !result?.success) {
|
|
76
|
+
const err = result?.error;
|
|
77
|
+
const code = typeof err === 'object' ? (err?.code || 'unknown_error') : (err || 'unknown_error');
|
|
78
|
+
const detail = typeof err === 'object' ? err?.message : undefined;
|
|
79
|
+
throw new client_1.MyApiError(code, response.status, detail, typeof err === 'object' ? err : undefined);
|
|
80
|
+
}
|
|
81
|
+
return result.data;
|
|
82
|
+
}
|
package/dist/hq.d.ts
CHANGED
|
@@ -39,6 +39,25 @@ export interface AccountInfo {
|
|
|
39
39
|
account_id: string;
|
|
40
40
|
email?: string;
|
|
41
41
|
is_anonymous?: boolean;
|
|
42
|
+
spend_cap_cents?: number;
|
|
43
|
+
current_period_spend_cents?: number;
|
|
44
|
+
}
|
|
45
|
+
export type GrantAccess = 'read' | 'write';
|
|
46
|
+
export type Grants = Record<string, GrantAccess>;
|
|
47
|
+
export declare const GRANTABLE_SLOTS: readonly ["domain", "email", "funnel", "storage", "image", "webhook", "workflow", "function", "url", "people", "company", "audience", "llm", "database", "crm"];
|
|
48
|
+
export type KeyKind = 'account' | 'function' | 'manual';
|
|
49
|
+
export type SpendCapPeriod = 'month' | 'day' | 'none';
|
|
50
|
+
export interface ApiKey {
|
|
51
|
+
id: string;
|
|
52
|
+
name: string;
|
|
53
|
+
prefix: string;
|
|
54
|
+
kind: KeyKind;
|
|
55
|
+
org_id: string | null;
|
|
56
|
+
grants: Grants;
|
|
57
|
+
spend_cap_cents: number | null;
|
|
58
|
+
spend_cap_period: string;
|
|
59
|
+
current_period_spend_cents?: number;
|
|
60
|
+
api_key?: string;
|
|
42
61
|
}
|
|
43
62
|
export declare function createAnonymousAccount(): Promise<AuthResult & {
|
|
44
63
|
subdomain_url: string;
|
|
@@ -55,19 +74,21 @@ export type FreeTierEntry = {
|
|
|
55
74
|
};
|
|
56
75
|
export declare function getFreeTier(apiKey: string): Promise<FreeTierEntry[] | null>;
|
|
57
76
|
export declare function getAccount(apiKey: string): Promise<AccountInfo>;
|
|
58
|
-
export
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
}
|
|
63
|
-
export declare function
|
|
64
|
-
|
|
65
|
-
name: string;
|
|
66
|
-
prefix: string;
|
|
67
|
-
created_at: string;
|
|
68
|
-
last_used_at?: string;
|
|
69
|
-
}[]>;
|
|
77
|
+
export interface CreateApiKeyOptions {
|
|
78
|
+
grants?: Grants;
|
|
79
|
+
orgId?: string;
|
|
80
|
+
spendCapCents?: number;
|
|
81
|
+
}
|
|
82
|
+
export declare function createApiKey(apiKey: string, name: string, opts?: CreateApiKeyOptions): Promise<ApiKey>;
|
|
83
|
+
export declare function listApiKeys(apiKey: string): Promise<ApiKey[]>;
|
|
70
84
|
export declare function revokeApiKey(apiKey: string, keyId: string): Promise<void>;
|
|
85
|
+
export declare function revokeAllKeys(apiKey: string, kind?: KeyKind): Promise<{
|
|
86
|
+
revoked: number;
|
|
87
|
+
}>;
|
|
88
|
+
export declare function setAccountSpendCap(apiKey: string, spendCapCents: number | null, period?: SpendCapPeriod): Promise<{
|
|
89
|
+
spend_cap_cents: number | null;
|
|
90
|
+
spend_cap_period: string;
|
|
91
|
+
}>;
|
|
71
92
|
export declare function createOrg(apiKey: string, payload: OrgPayload): Promise<Org>;
|
|
72
93
|
export declare function importOrg(apiKey: string, orgId: string, domain: string, autoAccept?: boolean): Promise<{
|
|
73
94
|
job_id: string;
|
package/dist/hq.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.EXPOSES = void 0;
|
|
3
|
+
exports.GRANTABLE_SLOTS = exports.EXPOSES = void 0;
|
|
4
4
|
exports.createAnonymousAccount = createAnonymousAccount;
|
|
5
5
|
exports.sendCode = sendCode;
|
|
6
6
|
exports.verifyCode = verifyCode;
|
|
@@ -10,6 +10,8 @@ exports.getAccount = getAccount;
|
|
|
10
10
|
exports.createApiKey = createApiKey;
|
|
11
11
|
exports.listApiKeys = listApiKeys;
|
|
12
12
|
exports.revokeApiKey = revokeApiKey;
|
|
13
|
+
exports.revokeAllKeys = revokeAllKeys;
|
|
14
|
+
exports.setAccountSpendCap = setAccountSpendCap;
|
|
13
15
|
exports.createOrg = createOrg;
|
|
14
16
|
exports.importOrg = importOrg;
|
|
15
17
|
exports.getOrgImportStatus = getOrgImportStatus;
|
|
@@ -33,7 +35,9 @@ exports.EXPOSES = [
|
|
|
33
35
|
'GET /hq/account/free-tier',
|
|
34
36
|
'POST /hq/account/create/key',
|
|
35
37
|
'GET /hq/account/keys',
|
|
38
|
+
'POST /hq/account/keys/revoke-all',
|
|
36
39
|
'DELETE /hq/account/delete/key/{key_id}',
|
|
40
|
+
'PATCH /hq/account/spend-cap',
|
|
37
41
|
'POST /hq/orgs',
|
|
38
42
|
'GET /hq/orgs',
|
|
39
43
|
'GET /hq/orgs/{org_id}',
|
|
@@ -47,6 +51,13 @@ exports.EXPOSES = [
|
|
|
47
51
|
'POST /hq/billing/setup-payment',
|
|
48
52
|
'POST /hq/billing/topup',
|
|
49
53
|
];
|
|
54
|
+
// The closed grantable-slot vocabulary. Mirrors `iam.GrantableSlots` in the
|
|
55
|
+
// backend — keep in sync. Used to validate `--grant` client-side before the
|
|
56
|
+
// network call. "*" is also valid in a Grants map but is not a slot name.
|
|
57
|
+
exports.GRANTABLE_SLOTS = [
|
|
58
|
+
'domain', 'email', 'funnel', 'storage', 'image', 'webhook', 'workflow',
|
|
59
|
+
'function', 'url', 'people', 'company', 'audience', 'llm', 'database', 'crm',
|
|
60
|
+
];
|
|
50
61
|
async function createAnonymousAccount() {
|
|
51
62
|
return (0, client_1.request)('POST', `${config_1.HQ_BASE}/hq/account/anonymous`, undefined, {});
|
|
52
63
|
}
|
|
@@ -65,8 +76,18 @@ async function getFreeTier(apiKey) {
|
|
|
65
76
|
async function getAccount(apiKey) {
|
|
66
77
|
return (0, client_1.request)('GET', `${config_1.HQ_BASE}/hq/account/me`, apiKey);
|
|
67
78
|
}
|
|
68
|
-
|
|
69
|
-
|
|
79
|
+
// Mints a manual API key. The requested (grants, org, spend cap) must be a
|
|
80
|
+
// subset of the calling key's authority — the backend rejects escalation
|
|
81
|
+
// with 403 SCOPE_FORBIDDEN. The full `api_key` is in the response ONCE.
|
|
82
|
+
async function createApiKey(apiKey, name, opts = {}) {
|
|
83
|
+
const body = { name };
|
|
84
|
+
if (opts.grants)
|
|
85
|
+
body.grants = opts.grants;
|
|
86
|
+
if (opts.orgId)
|
|
87
|
+
body.org_id = opts.orgId;
|
|
88
|
+
if (opts.spendCapCents != null)
|
|
89
|
+
body.spend_cap_cents = opts.spendCapCents;
|
|
90
|
+
return (0, client_1.request)('POST', `${config_1.HQ_BASE}/hq/account/create/key`, apiKey, body);
|
|
70
91
|
}
|
|
71
92
|
async function listApiKeys(apiKey) {
|
|
72
93
|
return (0, client_1.request)('GET', `${config_1.HQ_BASE}/hq/account/keys`, apiKey);
|
|
@@ -74,6 +95,20 @@ async function listApiKeys(apiKey) {
|
|
|
74
95
|
async function revokeApiKey(apiKey, keyId) {
|
|
75
96
|
return (0, client_1.request)('DELETE', `${config_1.HQ_BASE}/hq/account/delete/key/${encodeURIComponent(keyId)}`, apiKey);
|
|
76
97
|
}
|
|
98
|
+
// The kill switch. With no `kind`, revokes every active key in the account —
|
|
99
|
+
// including the caller's own; recovery is re-auth via login. A `kind` narrows
|
|
100
|
+
// it to one provenance class.
|
|
101
|
+
async function revokeAllKeys(apiKey, kind) {
|
|
102
|
+
return (0, client_1.request)('POST', `${config_1.HQ_BASE}/hq/account/keys/revoke-all`, apiKey, kind ? { kind } : {});
|
|
103
|
+
}
|
|
104
|
+
// Sets or clears the account-level spend ceiling (IAM "Layer 2"). Pass
|
|
105
|
+
// `null` to clear. `period` defaults to 'month' server-side.
|
|
106
|
+
async function setAccountSpendCap(apiKey, spendCapCents, period) {
|
|
107
|
+
const body = { spend_cap_cents: spendCapCents };
|
|
108
|
+
if (period)
|
|
109
|
+
body.period = period;
|
|
110
|
+
return (0, client_1.request)('PATCH', `${config_1.HQ_BASE}/hq/account/spend-cap`, apiKey, body);
|
|
111
|
+
}
|
|
77
112
|
async function createOrg(apiKey, payload) {
|
|
78
113
|
return (0, client_1.request)('POST', `${config_1.HQ_BASE}/hq/orgs`, apiKey, payload);
|
|
79
114
|
}
|
package/dist/index.d.ts
CHANGED
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.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;
|
|
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,5 @@ 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"));
|
|
65
|
+
exports.payments = __importStar(require("./payments"));
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import type { Exposes } from './exposes';
|
|
2
|
+
export declare const EXPOSES: Exposes;
|
|
3
|
+
export interface ConnectStatus {
|
|
4
|
+
tier: string;
|
|
5
|
+
stripe_account_id: string;
|
|
6
|
+
onboarding_status: string;
|
|
7
|
+
application_fee_bps?: number;
|
|
8
|
+
}
|
|
9
|
+
export type ChargeInterval = 'month' | 'year';
|
|
10
|
+
export interface Charge {
|
|
11
|
+
id: string;
|
|
12
|
+
amount_cents: number;
|
|
13
|
+
currency: string;
|
|
14
|
+
every?: ChargeInterval;
|
|
15
|
+
description?: string;
|
|
16
|
+
customer_email?: string;
|
|
17
|
+
status: string;
|
|
18
|
+
created_at: string;
|
|
19
|
+
succeeded_at?: string;
|
|
20
|
+
refunded_at?: string;
|
|
21
|
+
}
|
|
22
|
+
export interface CreateChargeResponse {
|
|
23
|
+
payment_id: string;
|
|
24
|
+
checkout_url: string;
|
|
25
|
+
status: string;
|
|
26
|
+
}
|
|
27
|
+
export interface CreateChargePayload {
|
|
28
|
+
amount_cents: number;
|
|
29
|
+
currency?: string;
|
|
30
|
+
email?: string;
|
|
31
|
+
description?: string;
|
|
32
|
+
every?: ChargeInterval;
|
|
33
|
+
success_url?: string;
|
|
34
|
+
cancel_url?: string;
|
|
35
|
+
}
|
|
36
|
+
export declare function connect(apiKey: string, orgId: string, stripeSecretKey: string, tier?: 't0' | 't1'): Promise<ConnectStatus>;
|
|
37
|
+
export declare function getConnect(apiKey: string, orgId: string): Promise<ConnectStatus>;
|
|
38
|
+
export declare function createCharge(apiKey: string, orgId: string, payload: CreateChargePayload): Promise<CreateChargeResponse>;
|
|
39
|
+
export declare function listCharges(apiKey: string, orgId: string): Promise<Charge[]>;
|
|
40
|
+
export declare function getCharge(apiKey: string, orgId: string, chargeId: string): Promise<Charge>;
|
|
41
|
+
export declare function refundCharge(apiKey: string, orgId: string, chargeId: string): Promise<{
|
|
42
|
+
id: string;
|
|
43
|
+
status: string;
|
|
44
|
+
}>;
|
package/dist/payments.js
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.EXPOSES = void 0;
|
|
4
|
+
exports.connect = connect;
|
|
5
|
+
exports.getConnect = getConnect;
|
|
6
|
+
exports.createCharge = createCharge;
|
|
7
|
+
exports.listCharges = listCharges;
|
|
8
|
+
exports.getCharge = getCharge;
|
|
9
|
+
exports.refundCharge = refundCharge;
|
|
10
|
+
const client_1 = require("./client");
|
|
11
|
+
const config_1 = require("./config");
|
|
12
|
+
// Backend: my-payments-api T0 (BYO Stripe) per
|
|
13
|
+
// myapi-hq/internal/routes/payments/. T1 (Connect Express) is deferred —
|
|
14
|
+
// connect with tier 't1' returns 501. The webhook receiver is public
|
|
15
|
+
// (Stripe signs it); it is listed here for parity but has no SDK function.
|
|
16
|
+
exports.EXPOSES = [
|
|
17
|
+
'POST /payments/orgs/{org_id}/connect',
|
|
18
|
+
'GET /payments/orgs/{org_id}/connect',
|
|
19
|
+
'POST /payments/orgs/{org_id}/charges',
|
|
20
|
+
'GET /payments/orgs/{org_id}/charges',
|
|
21
|
+
'GET /payments/orgs/{org_id}/charges/{id}',
|
|
22
|
+
'POST /payments/orgs/{org_id}/charges/{id}/refund',
|
|
23
|
+
'POST /payments/webhook/{org_id}',
|
|
24
|
+
];
|
|
25
|
+
// connect links the org's own Stripe account (T0 — BYO secret key). The
|
|
26
|
+
// key is validated live against Stripe and stored encrypted; it never
|
|
27
|
+
// touches MyAPI Postgres or a log line. Pass a 't1' tier to see the
|
|
28
|
+
// 501 T1_DEFERRED path.
|
|
29
|
+
async function connect(apiKey, orgId, stripeSecretKey, tier = 't0') {
|
|
30
|
+
return (0, client_1.request)('POST', `${config_1.PAYMENTS_BASE}/payments/orgs/${encodeURIComponent(orgId)}/connect`, apiKey, {
|
|
31
|
+
tier,
|
|
32
|
+
stripe_secret_key: stripeSecretKey,
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
// getConnect reads the org's connection status. Throws NOT_CONNECTED (404)
|
|
36
|
+
// when the org has not connected Stripe.
|
|
37
|
+
async function getConnect(apiKey, orgId) {
|
|
38
|
+
return (0, client_1.request)('GET', `${config_1.PAYMENTS_BASE}/payments/orgs/${encodeURIComponent(orgId)}/connect`, apiKey);
|
|
39
|
+
}
|
|
40
|
+
// createCharge opens a Stripe Checkout Session on the org's connected
|
|
41
|
+
// account. `every` makes it a subscription; otherwise a one-off payment.
|
|
42
|
+
async function createCharge(apiKey, orgId, payload) {
|
|
43
|
+
return (0, client_1.request)('POST', `${config_1.PAYMENTS_BASE}/payments/orgs/${encodeURIComponent(orgId)}/charges`, apiKey, payload);
|
|
44
|
+
}
|
|
45
|
+
async function listCharges(apiKey, orgId) {
|
|
46
|
+
return (0, client_1.request)('GET', `${config_1.PAYMENTS_BASE}/payments/orgs/${encodeURIComponent(orgId)}/charges`, apiKey);
|
|
47
|
+
}
|
|
48
|
+
async function getCharge(apiKey, orgId, chargeId) {
|
|
49
|
+
return (0, client_1.request)('GET', `${config_1.PAYMENTS_BASE}/payments/orgs/${encodeURIComponent(orgId)}/charges/${encodeURIComponent(chargeId)}`, apiKey);
|
|
50
|
+
}
|
|
51
|
+
// refundCharge issues a full refund (v1 — partial refunds out of scope).
|
|
52
|
+
async function refundCharge(apiKey, orgId, chargeId) {
|
|
53
|
+
return (0, client_1.request)('POST', `${config_1.PAYMENTS_BASE}/payments/orgs/${encodeURIComponent(orgId)}/charges/${encodeURIComponent(chargeId)}/refund`, apiKey);
|
|
54
|
+
}
|
package/dist/services.js
CHANGED
|
@@ -187,4 +187,35 @@ 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: deploy/env/runs shipped (CF Workers bundle upload,
|
|
195
|
+
// invocation URL, Worker Secrets, run history). /logs still pending —
|
|
196
|
+
// 'preview' reflects "usable, not yet 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: 'preview',
|
|
203
|
+
keywords: k('function', 'serverless', 'edge', 'cloudflare-workers', 'http-handler', 'cron'),
|
|
204
|
+
},
|
|
205
|
+
{
|
|
206
|
+
// CLI top-level command + SDK namespace are both `payments`; skill
|
|
207
|
+
// directory is `my-payments-api`.
|
|
208
|
+
//
|
|
209
|
+
// Backend status: T0 (BYO Stripe) shipped — connect, charges (Checkout
|
|
210
|
+
// Sessions), refunds, per-org webhook. T1 (Connect Express) is deferred
|
|
211
|
+
// (connect returns 501 T1_DEFERRED). 'preview' reflects "T0 usable, T1
|
|
212
|
+
// pending."
|
|
213
|
+
module: 'payments',
|
|
214
|
+
skill: 'my-payments-api',
|
|
215
|
+
domain: 'mypaymentsapi.com',
|
|
216
|
+
description: 'Take payments with Stripe Checkout. Connect your Stripe account, create one-off or recurring charges, and refund — hosted checkout, no card handling.',
|
|
217
|
+
category: 'compute',
|
|
218
|
+
status: 'preview',
|
|
219
|
+
keywords: k('payments', 'stripe', 'checkout', 'billing', 'subscription'),
|
|
220
|
+
},
|
|
190
221
|
];
|
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
|
}
|
package/dist/workflow.d.ts
CHANGED
|
@@ -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
package/src/config.ts
CHANGED
|
@@ -12,6 +12,10 @@ 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;
|
|
18
|
+
export const PAYMENTS_BASE= process.env.MYAPI_PAYMENTS_URL?? GATEWAY;
|
|
15
19
|
export const IMAGE_BASE = process.env.MYAPI_IMAGE_URL ?? 'https://api.myimageapi.com';
|
|
16
20
|
export const WEBHOOK_BASE = process.env.MYAPI_WEBHOOK_URL ?? 'https://api.mywebhookapi.com';
|
|
17
21
|
export const WORKFLOW_BASE= process.env.MYAPI_WORKFLOW_URL?? 'https://api.myworkflowapi.com';
|
package/src/domain.ts
CHANGED
|
@@ -125,11 +125,29 @@ export async function getDomainStatus(apiKey: string, orgId: string, domain: str
|
|
|
125
125
|
return request('GET', `${BASE_URL}/domain/orgs/${encodeURIComponent(orgId)}/${encodeURIComponent(domain)}/status`, apiKey);
|
|
126
126
|
}
|
|
127
127
|
|
|
128
|
-
|
|
129
|
-
|
|
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);
|
|
130
148
|
}
|
|
131
149
|
|
|
132
|
-
export async function unassignDomain(apiKey: string, orgId: string, domain: string): Promise<
|
|
150
|
+
export async function unassignDomain(apiKey: string, orgId: string, domain: string): Promise<AssignDomainResponse> {
|
|
133
151
|
return request('POST', `${BASE_URL}/domain/orgs/${encodeURIComponent(orgId)}/${encodeURIComponent(domain)}/assign`, apiKey, { org_id: null });
|
|
134
152
|
}
|
|
135
153
|
|
package/src/function.ts
ADDED
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
import { request, MyApiError } from './client';
|
|
2
|
+
import { ApiResponse } from './types';
|
|
3
|
+
import { FUNCTION_BASE as BASE_URL } from './config';
|
|
4
|
+
import type { Exposes } from './exposes';
|
|
5
|
+
|
|
6
|
+
// Backend: Story 1 (metadata + scoped API key) and Story 2/4/5 (deploy,
|
|
7
|
+
// runs, env) per myapi-hq/internal/routes/function/. The /logs endpoint is
|
|
8
|
+
// still pending — do not add forward-compat fields the backend will ignore.
|
|
9
|
+
export const EXPOSES: Exposes = [
|
|
10
|
+
'POST /function/orgs/{org_id}/functions',
|
|
11
|
+
'GET /function/orgs/{org_id}/functions',
|
|
12
|
+
'GET /function/orgs/{org_id}/functions/{id}',
|
|
13
|
+
'DELETE /function/orgs/{org_id}/functions/{id}',
|
|
14
|
+
'POST /function/orgs/{org_id}/functions/{id}/bundle',
|
|
15
|
+
'POST /function/orgs/{org_id}/functions/{id}/env',
|
|
16
|
+
'GET /function/orgs/{org_id}/functions/{id}/runs',
|
|
17
|
+
];
|
|
18
|
+
|
|
19
|
+
export type TriggerType = 'http' | 'cron';
|
|
20
|
+
|
|
21
|
+
// Mirrors the backend's `Function` struct in crud.go. `invocation_url`
|
|
22
|
+
// stays an empty string until the function is deployed (uploadBundle).
|
|
23
|
+
export interface Fn {
|
|
24
|
+
id: string;
|
|
25
|
+
org_id: string;
|
|
26
|
+
name: string;
|
|
27
|
+
trigger_type: TriggerType;
|
|
28
|
+
cron_schedule?: string;
|
|
29
|
+
invocation_url: string;
|
|
30
|
+
created_at: string;
|
|
31
|
+
updated_at: string;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export interface CreatePayload {
|
|
35
|
+
name: string; // required; ^[a-z0-9][a-z0-9-]{0,49}$
|
|
36
|
+
trigger_type?: TriggerType; // optional, defaults to 'http' server-side
|
|
37
|
+
cron_schedule?: string; // required if trigger_type === 'cron'
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// POST response envelope includes the scoped API key — returned ONCE. The
|
|
41
|
+
// caller must persist it if they want to use it directly; the SDK does not
|
|
42
|
+
// store it. The function's runtime shim (`globalThis.MYAPI`) uses this key
|
|
43
|
+
// internally.
|
|
44
|
+
export interface CreateResponse {
|
|
45
|
+
function: Fn;
|
|
46
|
+
scoped_api_key: string;
|
|
47
|
+
scoped_api_key_id: string;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// uploadBundle response. The scoped API key is ROTATED on every deploy —
|
|
51
|
+
// the value here is fresh and the only time it is knowable. The previous
|
|
52
|
+
// scoped key is revoked server-side.
|
|
53
|
+
export interface DeployResponse {
|
|
54
|
+
function: Fn;
|
|
55
|
+
invocation_url: string;
|
|
56
|
+
scoped_api_key: string;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// One recorded invocation — mirrors the backend's `FunctionRun` struct.
|
|
60
|
+
export interface FunctionRun {
|
|
61
|
+
id: string;
|
|
62
|
+
function_id: string;
|
|
63
|
+
invoked_at: string;
|
|
64
|
+
duration_ms?: number;
|
|
65
|
+
status?: string;
|
|
66
|
+
error_message?: string;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
export async function createFunction(apiKey: string, orgId: string, payload: CreatePayload): Promise<CreateResponse> {
|
|
70
|
+
return request('POST', `${BASE_URL}/function/orgs/${encodeURIComponent(orgId)}/functions`, apiKey, payload);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
export async function listFunctions(apiKey: string, orgId: string): Promise<Fn[]> {
|
|
74
|
+
return request('GET', `${BASE_URL}/function/orgs/${encodeURIComponent(orgId)}/functions`, apiKey);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
export async function getFunction(apiKey: string, orgId: string, fnId: string): Promise<Fn> {
|
|
78
|
+
return request('GET', `${BASE_URL}/function/orgs/${encodeURIComponent(orgId)}/functions/${encodeURIComponent(fnId)}`, apiKey);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
export async function deleteFunction(apiKey: string, orgId: string, fnId: string): Promise<void> {
|
|
82
|
+
return request('DELETE', `${BASE_URL}/function/orgs/${encodeURIComponent(orgId)}/functions/${encodeURIComponent(fnId)}`, apiKey);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// uploadBundle deploys a single-file JS bundle (Story 2). The backend
|
|
86
|
+
// wraps it with the MYAPI shim and uploads to Cloudflare Workers. The
|
|
87
|
+
// multipart field MUST be named `bundle`. Raw upload cap is 4MB.
|
|
88
|
+
export async function uploadBundle(
|
|
89
|
+
apiKey: string,
|
|
90
|
+
orgId: string,
|
|
91
|
+
fnId: string,
|
|
92
|
+
bundle: Blob | Buffer | string,
|
|
93
|
+
filename = 'bundle.js',
|
|
94
|
+
): Promise<DeployResponse> {
|
|
95
|
+
const formData = new FormData();
|
|
96
|
+
formData.append('bundle', new Blob([bundle as any], { type: 'application/javascript' }), filename);
|
|
97
|
+
|
|
98
|
+
const response = await fetch(
|
|
99
|
+
`${BASE_URL}/function/orgs/${encodeURIComponent(orgId)}/functions/${encodeURIComponent(fnId)}/bundle`,
|
|
100
|
+
{ method: 'POST', headers: { Authorization: `Bearer ${apiKey}` }, body: formData as any },
|
|
101
|
+
);
|
|
102
|
+
|
|
103
|
+
let result: any;
|
|
104
|
+
try {
|
|
105
|
+
result = await response.json();
|
|
106
|
+
} catch {
|
|
107
|
+
throw new MyApiError('invalid_json_response', response.status);
|
|
108
|
+
}
|
|
109
|
+
if (!response.ok || !result?.success) {
|
|
110
|
+
const err = result?.error;
|
|
111
|
+
const code = typeof err === 'object' ? (err?.code || 'unknown_error') : (err || 'unknown_error');
|
|
112
|
+
const detail = typeof err === 'object' ? err?.message : undefined;
|
|
113
|
+
throw new MyApiError(code, response.status, detail, typeof err === 'object' ? err : undefined);
|
|
114
|
+
}
|
|
115
|
+
return (result as ApiResponse<DeployResponse>).data as DeployResponse;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// setFunctionEnv writes a secret (Stripe key, etc.) as a Cloudflare Worker
|
|
119
|
+
// Secret on the deployed script (Story 5). The function must already be
|
|
120
|
+
// deployed. The value is never stored in MyAPI Postgres or returned.
|
|
121
|
+
export async function setFunctionEnv(apiKey: string, orgId: string, fnId: string, name: string, value: string): Promise<void> {
|
|
122
|
+
return request('POST', `${BASE_URL}/function/orgs/${encodeURIComponent(orgId)}/functions/${encodeURIComponent(fnId)}/env`, apiKey, { name, value });
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
// listFunctionRuns returns recent invocation records, most recent first
|
|
126
|
+
// (Story 4). Capped at 100 server-side.
|
|
127
|
+
export async function listFunctionRuns(apiKey: string, orgId: string, fnId: string): Promise<FunctionRun[]> {
|
|
128
|
+
return request('GET', `${BASE_URL}/function/orgs/${encodeURIComponent(orgId)}/functions/${encodeURIComponent(fnId)}/runs`, apiKey);
|
|
129
|
+
}
|
package/src/funnel.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 { FUNNEL_BASE as BASE_URL } from './config';
|
|
3
4
|
import type { Exposes } from './exposes';
|
|
4
5
|
|
|
@@ -10,11 +11,19 @@ export const EXPOSES: Exposes = [
|
|
|
10
11
|
'POST /funnel/orgs/{org_id}/funnels/{funnel_id}/push-page',
|
|
11
12
|
'POST /funnel/orgs/{org_id}/funnels/{funnel_id}/verify',
|
|
12
13
|
'GET /funnel/orgs/{org_id}/funnels/{funnel_id}/pages',
|
|
14
|
+
'POST /funnel/orgs/{org_id}/funnels/{funnel_id}/files',
|
|
13
15
|
];
|
|
14
16
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
17
|
+
export interface Funnel {
|
|
18
|
+
id: string;
|
|
19
|
+
org_id: string;
|
|
20
|
+
// Backend (2026-05-15): POST /funnels now accepts optional `name`. Defaults
|
|
21
|
+
// to the org's preview_subdomain for back-compat. Get / list responses
|
|
22
|
+
// include it when set.
|
|
23
|
+
name?: string;
|
|
24
|
+
created_at: string;
|
|
25
|
+
updated_at: string;
|
|
26
|
+
}
|
|
18
27
|
export interface CreateFunnelResponse { funnel: Funnel; subdomain_url?: string; domain_url?: string }
|
|
19
28
|
export interface VerifyResult {
|
|
20
29
|
pages?: Array<{ slug: string; tests: VerifyTest[] }>;
|
|
@@ -22,8 +31,10 @@ export interface VerifyResult {
|
|
|
22
31
|
}
|
|
23
32
|
export interface VerifyTest { type: 'link' | 'webhook'; url: string; passed: boolean; details: string }
|
|
24
33
|
|
|
25
|
-
export async function createFunnel(apiKey: string, orgId: string): Promise<CreateFunnelResponse> {
|
|
26
|
-
|
|
34
|
+
export async function createFunnel(apiKey: string, orgId: string, opts?: { name?: string }): Promise<CreateFunnelResponse> {
|
|
35
|
+
// v2: accept optional name for N-funnels-per-org. Backend rejects names
|
|
36
|
+
// matching the reserved-suffix rules; agent should pre-validate.
|
|
37
|
+
return request('POST', `${BASE_URL}/funnel/orgs/${encodeURIComponent(orgId)}/funnels`, apiKey, opts?.name ? { name: opts.name } : {});
|
|
27
38
|
}
|
|
28
39
|
export async function getFunnel(apiKey: string, orgId: string, funnelId: string): Promise<Funnel> {
|
|
29
40
|
return request('GET', `${BASE_URL}/funnel/orgs/${encodeURIComponent(orgId)}/funnels/${encodeURIComponent(funnelId)}`, apiKey);
|
|
@@ -64,3 +75,66 @@ export async function listFunnelPages(apiKey: string, orgId: string, funnelId: s
|
|
|
64
75
|
export async function verifyFunnel(apiKey: string, orgId: string, funnelId: string, opts?: { html?: string; slug?: string }): Promise<VerifyResult> {
|
|
65
76
|
return request('POST', `${BASE_URL}/funnel/orgs/${encodeURIComponent(orgId)}/funnels/${encodeURIComponent(funnelId)}/verify`, apiKey, opts || {});
|
|
66
77
|
}
|
|
78
|
+
|
|
79
|
+
// One file in a directory publish: `path` is the path within the site
|
|
80
|
+
// (e.g. "index.html", "assets/app.js"); the backend roots it ("/index.html").
|
|
81
|
+
export interface PublishFile {
|
|
82
|
+
path: string;
|
|
83
|
+
content: Blob | Buffer | string;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
export interface PublishOptions {
|
|
87
|
+
// 'dev' or 'prod' channel — defaults to 'prod' server-side.
|
|
88
|
+
env?: 'dev' | 'prod';
|
|
89
|
+
// Force SPA fallback. Auto-true server-side when an index.html is present.
|
|
90
|
+
spaMode?: boolean;
|
|
91
|
+
// Bind /api/* on the funnel to a deployed function.
|
|
92
|
+
apiFunctionId?: string;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
export interface PublishResult {
|
|
96
|
+
manifest_id: string;
|
|
97
|
+
channel: 'dev' | 'prod';
|
|
98
|
+
file_count: number;
|
|
99
|
+
size_bytes: number;
|
|
100
|
+
spa_mode: boolean;
|
|
101
|
+
published_url: string;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// publishFiles is the my-funnel-api v2 directory publish. Every file is a
|
|
105
|
+
// multipart part named `files`; the part's filename carries the site path.
|
|
106
|
+
// 25MB total cap server-side.
|
|
107
|
+
export async function publishFiles(
|
|
108
|
+
apiKey: string,
|
|
109
|
+
orgId: string,
|
|
110
|
+
funnelId: string,
|
|
111
|
+
files: PublishFile[],
|
|
112
|
+
opts: PublishOptions = {},
|
|
113
|
+
): Promise<PublishResult> {
|
|
114
|
+
const formData = new FormData();
|
|
115
|
+
for (const f of files) {
|
|
116
|
+
formData.append('files', new Blob([f.content as any]), f.path);
|
|
117
|
+
}
|
|
118
|
+
if (opts.env) formData.append('env', opts.env);
|
|
119
|
+
if (opts.spaMode !== undefined) formData.append('spa_mode', String(opts.spaMode));
|
|
120
|
+
if (opts.apiFunctionId) formData.append('api_function_id', opts.apiFunctionId);
|
|
121
|
+
|
|
122
|
+
const response = await fetch(
|
|
123
|
+
`${BASE_URL}/funnel/orgs/${encodeURIComponent(orgId)}/funnels/${encodeURIComponent(funnelId)}/files`,
|
|
124
|
+
{ method: 'POST', headers: { Authorization: `Bearer ${apiKey}` }, body: formData as any },
|
|
125
|
+
);
|
|
126
|
+
|
|
127
|
+
let result: any;
|
|
128
|
+
try {
|
|
129
|
+
result = await response.json();
|
|
130
|
+
} catch {
|
|
131
|
+
throw new MyApiError('invalid_json_response', response.status);
|
|
132
|
+
}
|
|
133
|
+
if (!response.ok || !result?.success) {
|
|
134
|
+
const err = result?.error;
|
|
135
|
+
const code = typeof err === 'object' ? (err?.code || 'unknown_error') : (err || 'unknown_error');
|
|
136
|
+
const detail = typeof err === 'object' ? err?.message : undefined;
|
|
137
|
+
throw new MyApiError(code, response.status, detail, typeof err === 'object' ? err : undefined);
|
|
138
|
+
}
|
|
139
|
+
return (result as ApiResponse<PublishResult>).data as PublishResult;
|
|
140
|
+
}
|
package/src/hq.ts
CHANGED
|
@@ -11,7 +11,9 @@ export const EXPOSES: Exposes = [
|
|
|
11
11
|
'GET /hq/account/free-tier',
|
|
12
12
|
'POST /hq/account/create/key',
|
|
13
13
|
'GET /hq/account/keys',
|
|
14
|
+
'POST /hq/account/keys/revoke-all',
|
|
14
15
|
'DELETE /hq/account/delete/key/{key_id}',
|
|
16
|
+
'PATCH /hq/account/spend-cap',
|
|
15
17
|
'POST /hq/orgs',
|
|
16
18
|
'GET /hq/orgs',
|
|
17
19
|
'GET /hq/orgs/{org_id}',
|
|
@@ -68,6 +70,44 @@ export interface AccountInfo {
|
|
|
68
70
|
account_id: string;
|
|
69
71
|
email?: string;
|
|
70
72
|
is_anonymous?: boolean;
|
|
73
|
+
// Account-level spend cap (IAM "Layer 2"). Present only when a cap is set.
|
|
74
|
+
spend_cap_cents?: number;
|
|
75
|
+
current_period_spend_cents?: number;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// ── Capability IAM (design-iam-capability-keys-2026-05-15) ───────────────────
|
|
79
|
+
|
|
80
|
+
export type GrantAccess = 'read' | 'write';
|
|
81
|
+
|
|
82
|
+
// A key's slot grants: slot name (or "*" wildcard) → access level.
|
|
83
|
+
// `write` implies `read`. A slot absent from the map = no access.
|
|
84
|
+
export type Grants = Record<string, GrantAccess>;
|
|
85
|
+
|
|
86
|
+
// The closed grantable-slot vocabulary. Mirrors `iam.GrantableSlots` in the
|
|
87
|
+
// backend — keep in sync. Used to validate `--grant` client-side before the
|
|
88
|
+
// network call. "*" is also valid in a Grants map but is not a slot name.
|
|
89
|
+
export const GRANTABLE_SLOTS = [
|
|
90
|
+
'domain', 'email', 'funnel', 'storage', 'image', 'webhook', 'workflow',
|
|
91
|
+
'function', 'url', 'people', 'company', 'audience', 'llm', 'database', 'crm',
|
|
92
|
+
] as const;
|
|
93
|
+
|
|
94
|
+
export type KeyKind = 'account' | 'function' | 'manual';
|
|
95
|
+
export type SpendCapPeriod = 'month' | 'day' | 'none';
|
|
96
|
+
|
|
97
|
+
// Mirrors the backend `keyView`. `api_key` is present ONLY in the create
|
|
98
|
+
// response (returned once). `current_period_spend_cents` is present on list
|
|
99
|
+
// (metered), absent on create (a new key has no spend yet).
|
|
100
|
+
export interface ApiKey {
|
|
101
|
+
id: string;
|
|
102
|
+
name: string;
|
|
103
|
+
prefix: string;
|
|
104
|
+
kind: KeyKind;
|
|
105
|
+
org_id: string | null;
|
|
106
|
+
grants: Grants;
|
|
107
|
+
spend_cap_cents: number | null;
|
|
108
|
+
spend_cap_period: string;
|
|
109
|
+
current_period_spend_cents?: number;
|
|
110
|
+
api_key?: string;
|
|
71
111
|
}
|
|
72
112
|
|
|
73
113
|
export async function createAnonymousAccount(): Promise<AuthResult & { subdomain_url: string }> {
|
|
@@ -106,11 +146,27 @@ export async function getAccount(apiKey: string): Promise<AccountInfo> {
|
|
|
106
146
|
return request('GET', `${BASE_URL}/hq/account/me`, apiKey);
|
|
107
147
|
}
|
|
108
148
|
|
|
109
|
-
export
|
|
110
|
-
|
|
149
|
+
export interface CreateApiKeyOptions {
|
|
150
|
+
// Slot grants. Omitted → backend defaults to unrestricted ({"*":"write"}).
|
|
151
|
+
grants?: Grants;
|
|
152
|
+
// Lock the key to a single org. Omitted → account-wide.
|
|
153
|
+
orgId?: string;
|
|
154
|
+
// Per-key spend ceiling, in cents. Omitted → no per-key cap.
|
|
155
|
+
spendCapCents?: number;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
// Mints a manual API key. The requested (grants, org, spend cap) must be a
|
|
159
|
+
// subset of the calling key's authority — the backend rejects escalation
|
|
160
|
+
// with 403 SCOPE_FORBIDDEN. The full `api_key` is in the response ONCE.
|
|
161
|
+
export async function createApiKey(apiKey: string, name: string, opts: CreateApiKeyOptions = {}): Promise<ApiKey> {
|
|
162
|
+
const body: Record<string, unknown> = { name };
|
|
163
|
+
if (opts.grants) body.grants = opts.grants;
|
|
164
|
+
if (opts.orgId) body.org_id = opts.orgId;
|
|
165
|
+
if (opts.spendCapCents != null) body.spend_cap_cents = opts.spendCapCents;
|
|
166
|
+
return request('POST', `${BASE_URL}/hq/account/create/key`, apiKey, body);
|
|
111
167
|
}
|
|
112
168
|
|
|
113
|
-
export async function listApiKeys(apiKey: string): Promise<
|
|
169
|
+
export async function listApiKeys(apiKey: string): Promise<ApiKey[]> {
|
|
114
170
|
return request('GET', `${BASE_URL}/hq/account/keys`, apiKey);
|
|
115
171
|
}
|
|
116
172
|
|
|
@@ -118,6 +174,25 @@ export async function revokeApiKey(apiKey: string, keyId: string): Promise<void>
|
|
|
118
174
|
return request('DELETE', `${BASE_URL}/hq/account/delete/key/${encodeURIComponent(keyId)}`, apiKey);
|
|
119
175
|
}
|
|
120
176
|
|
|
177
|
+
// The kill switch. With no `kind`, revokes every active key in the account —
|
|
178
|
+
// including the caller's own; recovery is re-auth via login. A `kind` narrows
|
|
179
|
+
// it to one provenance class.
|
|
180
|
+
export async function revokeAllKeys(apiKey: string, kind?: KeyKind): Promise<{ revoked: number }> {
|
|
181
|
+
return request('POST', `${BASE_URL}/hq/account/keys/revoke-all`, apiKey, kind ? { kind } : {});
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
// Sets or clears the account-level spend ceiling (IAM "Layer 2"). Pass
|
|
185
|
+
// `null` to clear. `period` defaults to 'month' server-side.
|
|
186
|
+
export async function setAccountSpendCap(
|
|
187
|
+
apiKey: string,
|
|
188
|
+
spendCapCents: number | null,
|
|
189
|
+
period?: SpendCapPeriod,
|
|
190
|
+
): Promise<{ spend_cap_cents: number | null; spend_cap_period: string }> {
|
|
191
|
+
const body: Record<string, unknown> = { spend_cap_cents: spendCapCents };
|
|
192
|
+
if (period) body.period = period;
|
|
193
|
+
return request('PATCH', `${BASE_URL}/hq/account/spend-cap`, apiKey, body);
|
|
194
|
+
}
|
|
195
|
+
|
|
121
196
|
export async function createOrg(apiKey: string, payload: OrgPayload): Promise<Org> {
|
|
122
197
|
return request('POST', `${BASE_URL}/hq/orgs`, apiKey, payload);
|
|
123
198
|
}
|
package/src/index.ts
CHANGED
package/src/payments.ts
ADDED
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import { request } from './client';
|
|
2
|
+
import { PAYMENTS_BASE as BASE_URL } from './config';
|
|
3
|
+
import type { Exposes } from './exposes';
|
|
4
|
+
|
|
5
|
+
// Backend: my-payments-api T0 (BYO Stripe) per
|
|
6
|
+
// myapi-hq/internal/routes/payments/. T1 (Connect Express) is deferred —
|
|
7
|
+
// connect with tier 't1' returns 501. The webhook receiver is public
|
|
8
|
+
// (Stripe signs it); it is listed here for parity but has no SDK function.
|
|
9
|
+
export const EXPOSES: Exposes = [
|
|
10
|
+
'POST /payments/orgs/{org_id}/connect',
|
|
11
|
+
'GET /payments/orgs/{org_id}/connect',
|
|
12
|
+
'POST /payments/orgs/{org_id}/charges',
|
|
13
|
+
'GET /payments/orgs/{org_id}/charges',
|
|
14
|
+
'GET /payments/orgs/{org_id}/charges/{id}',
|
|
15
|
+
'POST /payments/orgs/{org_id}/charges/{id}/refund',
|
|
16
|
+
'POST /payments/webhook/{org_id}',
|
|
17
|
+
];
|
|
18
|
+
|
|
19
|
+
// Connection status — the org's Stripe link. T0 takes no platform fee, so
|
|
20
|
+
// `application_fee_bps` is 0.
|
|
21
|
+
export interface ConnectStatus {
|
|
22
|
+
tier: string;
|
|
23
|
+
stripe_account_id: string;
|
|
24
|
+
onboarding_status: string;
|
|
25
|
+
application_fee_bps?: number;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// Charge billing interval. Absent = a one-off payment.
|
|
29
|
+
export type ChargeInterval = 'month' | 'year';
|
|
30
|
+
|
|
31
|
+
// A charge — mirrors the backend's `chargeView`.
|
|
32
|
+
export interface Charge {
|
|
33
|
+
id: string;
|
|
34
|
+
amount_cents: number;
|
|
35
|
+
currency: string;
|
|
36
|
+
every?: ChargeInterval;
|
|
37
|
+
description?: string;
|
|
38
|
+
customer_email?: string;
|
|
39
|
+
status: string;
|
|
40
|
+
created_at: string;
|
|
41
|
+
succeeded_at?: string;
|
|
42
|
+
refunded_at?: string;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// CreateCharge returns a hosted Stripe Checkout URL the agent pastes into
|
|
46
|
+
// its frontend — not a completed payment.
|
|
47
|
+
export interface CreateChargeResponse {
|
|
48
|
+
payment_id: string;
|
|
49
|
+
checkout_url: string;
|
|
50
|
+
status: string;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export interface CreateChargePayload {
|
|
54
|
+
amount_cents: number; // required, > 0
|
|
55
|
+
currency?: string; // defaults to 'usd' server-side
|
|
56
|
+
email?: string; // pre-fills the Checkout customer
|
|
57
|
+
description?: string; // shows on the Checkout line item
|
|
58
|
+
every?: ChargeInterval; // present = a subscription
|
|
59
|
+
success_url?: string;
|
|
60
|
+
cancel_url?: string;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// connect links the org's own Stripe account (T0 — BYO secret key). The
|
|
64
|
+
// key is validated live against Stripe and stored encrypted; it never
|
|
65
|
+
// touches MyAPI Postgres or a log line. Pass a 't1' tier to see the
|
|
66
|
+
// 501 T1_DEFERRED path.
|
|
67
|
+
export async function connect(apiKey: string, orgId: string, stripeSecretKey: string, tier: 't0' | 't1' = 't0'): Promise<ConnectStatus> {
|
|
68
|
+
return request('POST', `${BASE_URL}/payments/orgs/${encodeURIComponent(orgId)}/connect`, apiKey, {
|
|
69
|
+
tier,
|
|
70
|
+
stripe_secret_key: stripeSecretKey,
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// getConnect reads the org's connection status. Throws NOT_CONNECTED (404)
|
|
75
|
+
// when the org has not connected Stripe.
|
|
76
|
+
export async function getConnect(apiKey: string, orgId: string): Promise<ConnectStatus> {
|
|
77
|
+
return request('GET', `${BASE_URL}/payments/orgs/${encodeURIComponent(orgId)}/connect`, apiKey);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// createCharge opens a Stripe Checkout Session on the org's connected
|
|
81
|
+
// account. `every` makes it a subscription; otherwise a one-off payment.
|
|
82
|
+
export async function createCharge(apiKey: string, orgId: string, payload: CreateChargePayload): Promise<CreateChargeResponse> {
|
|
83
|
+
return request('POST', `${BASE_URL}/payments/orgs/${encodeURIComponent(orgId)}/charges`, apiKey, payload);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
export async function listCharges(apiKey: string, orgId: string): Promise<Charge[]> {
|
|
87
|
+
return request('GET', `${BASE_URL}/payments/orgs/${encodeURIComponent(orgId)}/charges`, apiKey);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
export async function getCharge(apiKey: string, orgId: string, chargeId: string): Promise<Charge> {
|
|
91
|
+
return request('GET', `${BASE_URL}/payments/orgs/${encodeURIComponent(orgId)}/charges/${encodeURIComponent(chargeId)}`, apiKey);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// refundCharge issues a full refund (v1 — partial refunds out of scope).
|
|
95
|
+
export async function refundCharge(apiKey: string, orgId: string, chargeId: string): Promise<{ id: string; status: string }> {
|
|
96
|
+
return request('POST', `${BASE_URL}/payments/orgs/${encodeURIComponent(orgId)}/charges/${encodeURIComponent(chargeId)}/refund`, apiKey);
|
|
97
|
+
}
|
package/src/services.ts
CHANGED
|
@@ -217,4 +217,35 @@ 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: deploy/env/runs shipped (CF Workers bundle upload,
|
|
225
|
+
// invocation URL, Worker Secrets, run history). /logs still pending —
|
|
226
|
+
// 'preview' reflects "usable, not yet 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: 'preview',
|
|
233
|
+
keywords: k('function', 'serverless', 'edge', 'cloudflare-workers', 'http-handler', 'cron'),
|
|
234
|
+
},
|
|
235
|
+
{
|
|
236
|
+
// CLI top-level command + SDK namespace are both `payments`; skill
|
|
237
|
+
// directory is `my-payments-api`.
|
|
238
|
+
//
|
|
239
|
+
// Backend status: T0 (BYO Stripe) shipped — connect, charges (Checkout
|
|
240
|
+
// Sessions), refunds, per-org webhook. T1 (Connect Express) is deferred
|
|
241
|
+
// (connect returns 501 T1_DEFERRED). 'preview' reflects "T0 usable, T1
|
|
242
|
+
// pending."
|
|
243
|
+
module: 'payments',
|
|
244
|
+
skill: 'my-payments-api',
|
|
245
|
+
domain: 'mypaymentsapi.com',
|
|
246
|
+
description: 'Take payments with Stripe Checkout. Connect your Stripe account, create one-off or recurring charges, and refund — hosted checkout, no card handling.',
|
|
247
|
+
category: 'compute',
|
|
248
|
+
status: 'preview',
|
|
249
|
+
keywords: k('payments', 'stripe', 'checkout', 'billing', 'subscription'),
|
|
250
|
+
},
|
|
220
251
|
] 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;
|