apinow-sdk 0.24.0 → 0.26.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -59,9 +59,18 @@ Returns:
59
59
  | `discoverPrice(url, method?)` | Discover the x402 price of any URL (free) |
60
60
  | `search(query, limit?)` | Semantic search across all endpoints |
61
61
  | `info(ns, name)` | Get endpoint details (free) |
62
- | `listWorkflows(opts?)` | List workflows (free) |
63
- | `getWorkflow(id)` | Get workflow details (free) |
62
+ | `listWorkflows(opts?)` | List workflows (filter by `creator`, `status`) |
63
+ | `listMyWorkflows(opts?)` | List workflows created by your wallet |
64
+ | `getWorkflow(id)` | Get workflow details (incl. `currentVersion`, `creatorWallet`) |
65
+ | `createWorkflow(config)` | Create a workflow (seeds v1) |
66
+ | `updateWorkflow(id, updates)` | Update a workflow (auto-bumps version on graph/price/splits change) |
67
+ | `deleteWorkflow(id)` | Delete a workflow |
64
68
  | `runWorkflow(id, input)` | Run a workflow with automatic x402 payment |
69
+ | `listWorkflowVersions(id)` | List versions (free) |
70
+ | `getWorkflowVersion(id, vid)` | Get a specific version |
71
+ | `createWorkflowVersion(id, updates)` | Creator — new version, defaults to active |
72
+ | `setDefaultWorkflowVersion(id, vid)` | Promote/rollback a version |
73
+ | `deleteWorkflowVersion(id, vid)` | Delete a non-default version |
65
74
  | `generateUI(opts)` | Start AI UI generation for an endpoint (async) |
66
75
  | `generateUIAndWait(opts)` | Generate UI and poll until complete |
67
76
  | `getGeneratedUI(id)` | Get a generated UI by ID |
@@ -74,22 +83,44 @@ Returns:
74
83
 
75
84
  ### Workflows
76
85
 
77
- Workflows chain multiple x402 endpoints into a single paid DAG pipeline with automatic payment splitting.
86
+ Workflows chain multiple x402 endpoints into a single paid DAG pipeline with automatic payment splitting. Each workflow is owned by a `creatorWallet` and tracks an immutable version history.
78
87
 
79
88
  ```typescript
80
- // list workflows
81
- const { workflows } = await apinow.listWorkflows({ status: 'active', limit: 10 });
89
+ // list workflows (optionally filter by creator)
90
+ const { workflows } = await apinow.listWorkflows({ creator: '0x...', status: 'active' });
82
91
 
83
- // get workflow details (nodes, splits, pricing)
84
- const workflow = await apinow.getWorkflow('90931d9c8fb94df9');
85
- console.log(workflow.name, workflow.totalPrice, workflow.graph.nodes);
92
+ // your own workflows
93
+ const mine = await apinow.listMyWorkflows();
94
+
95
+ // get workflow details (nodes, splits, pricing, currentVersion, creatorWallet)
96
+ const workflow = await apinow.getWorkflow('f5d40784593aa972');
86
97
 
87
98
  // run a workflow — x402 payment covers all nodes + creator split
88
- const result = await apinow.runWorkflow('90931d9c8fb94df9', {
99
+ const result = await apinow.runWorkflow('f5d40784593aa972', {
89
100
  query: 'birthday gift ideas for a friend who loves cooking',
90
101
  });
91
102
  ```
92
103
 
104
+ #### Versions & metadata cooldown
105
+
106
+ - `PUT /api/workflows/{id}` with changes to `graph`, `totalPrice`, or `splits` auto-creates a new version.
107
+ - `name` and `description` can only change **once every 7 days** per workflow (server returns `429` with `retryAfterMs`).
108
+ - To iterate freely, create a new version — no cooldown.
109
+
110
+ ```typescript
111
+ // snapshot history
112
+ const { versions } = await apinow.listWorkflowVersions('f5d40784593aa972');
113
+
114
+ // bump price without renaming
115
+ await apinow.createWorkflowVersion('f5d40784593aa972', {
116
+ totalPrice: '0.12',
117
+ changelog: 'Raised price after usage spike',
118
+ });
119
+
120
+ // rollback
121
+ await apinow.setDefaultWorkflowVersion('f5d40784593aa972', 1);
122
+ ```
123
+
93
124
  ### AI UI Generation
94
125
 
95
126
  Generate interactive Arrow JS sandbox UIs for any endpoint — ideal for AI agents that need a visual interface.
package/dist/cli.js CHANGED
@@ -7,7 +7,7 @@ const program = new Command();
7
7
  program
8
8
  .name('apinow')
9
9
  .description('CLI for APINow.fun — search, inspect, and call pay-per-request APIs')
10
- .version('0.24.0');
10
+ .version('0.26.0');
11
11
  // ─── Helpers ───
12
12
  function getPrivateKey(opts) {
13
13
  const raw = opts.key || process.env.APINOW_WALLET_PKEY || process.env.PRIVATE_KEY;
@@ -48,8 +48,14 @@ function getWallet(opts) {
48
48
  const account = privateKeyToAccount(privateKey);
49
49
  return { privateKey, address: account.address };
50
50
  }
51
- function walletHeaders(address) {
52
- return { 'Content-Type': 'application/json', 'x-wallet-address': address };
51
+ /**
52
+ * Signed headers for mutating calls. The backend verifies the signature
53
+ * recovers to the claimed wallet address before accepting the request.
54
+ */
55
+ async function walletHeaders(privateKey) {
56
+ const apinow = createClient({ privateKey });
57
+ const auth = await apinow.signAuthHeader();
58
+ return { 'Content-Type': 'application/json', ...auth };
53
59
  }
54
60
  // ─── search ───
55
61
  program
@@ -207,7 +213,7 @@ program
207
213
  .option('-k, --key <privateKey>', 'Wallet private key')
208
214
  .action(async (opts) => {
209
215
  try {
210
- const { address } = getWallet(opts);
216
+ const { privateKey, address } = getWallet(opts);
211
217
  const body = {
212
218
  namespace: opts.namespace,
213
219
  endpointName: opts.name,
@@ -218,7 +224,7 @@ program
218
224
  };
219
225
  const data = await fetchJson(`${API_BASE}/api/endpoints`, {
220
226
  method: 'POST',
221
- headers: walletHeaders(address),
227
+ headers: await walletHeaders(privateKey),
222
228
  body: JSON.stringify(body),
223
229
  });
224
230
  console.log(JSON.stringify(data, null, 2));
@@ -239,7 +245,7 @@ program
239
245
  .option('-k, --key <privateKey>', 'Wallet private key')
240
246
  .action(async (id, opts) => {
241
247
  try {
242
- const { address } = getWallet(opts);
248
+ const { privateKey, address } = getWallet(opts);
243
249
  const body = {};
244
250
  if (opts.description)
245
251
  body.description = opts.description;
@@ -251,7 +257,7 @@ program
251
257
  body.paymentOptions = [{ usdAmount: opts.price, amount: opts.price }];
252
258
  const data = await fetchJson(`${API_BASE}/api/endpoints/${id}`, {
253
259
  method: 'PUT',
254
- headers: walletHeaders(address),
260
+ headers: await walletHeaders(privateKey),
255
261
  body: JSON.stringify(body),
256
262
  });
257
263
  console.log(JSON.stringify(data, null, 2));
@@ -268,10 +274,10 @@ program
268
274
  .option('-k, --key <privateKey>', 'Wallet private key')
269
275
  .action(async (id, opts) => {
270
276
  try {
271
- const { address } = getWallet(opts);
277
+ const { privateKey, address } = getWallet(opts);
272
278
  const data = await fetchJson(`${API_BASE}/api/endpoints/${id}`, {
273
279
  method: 'DELETE',
274
- headers: walletHeaders(address),
280
+ headers: await walletHeaders(privateKey),
275
281
  });
276
282
  console.log(JSON.stringify(data, null, 2));
277
283
  }
@@ -425,6 +431,7 @@ program
425
431
  console.log(` ${data.description || '(no description)'}\n`);
426
432
  console.log(` ID: ${data.workflowId}`);
427
433
  console.log(` Status: ${data.status}`);
434
+ console.log(` Version: v${data.currentVersion ?? 1}${data.currentVersionId ? ` (${data.currentVersionId.slice(0, 8)}…)` : ''}`);
428
435
  console.log(` Cost: $${data.totalPrice} USDC`);
429
436
  console.log(` Chain: ${data.chain || 'base'}`);
430
437
  console.log(` Creator: ${data.creatorWallet}`);
@@ -486,7 +493,7 @@ program
486
493
  .option('-k, --key <privateKey>', 'Wallet private key')
487
494
  .action(async (opts) => {
488
495
  try {
489
- const { address } = getWallet(opts);
496
+ const { privateKey, address } = getWallet(opts);
490
497
  const body = { name: opts.name, totalPrice: opts.price };
491
498
  if (opts.description)
492
499
  body.description = opts.description;
@@ -498,7 +505,7 @@ program
498
505
  body.splits = JSON.parse(opts.splits);
499
506
  const data = await fetchJson(`${API_BASE}/api/workflows`, {
500
507
  method: 'POST',
501
- headers: walletHeaders(address),
508
+ headers: await walletHeaders(privateKey),
502
509
  body: JSON.stringify(body),
503
510
  });
504
511
  console.log(JSON.stringify(data, null, 2));
@@ -519,7 +526,7 @@ program
519
526
  .option('-k, --key <privateKey>', 'Wallet private key')
520
527
  .action(async (id, opts) => {
521
528
  try {
522
- const { address } = getWallet(opts);
529
+ const { privateKey, address } = getWallet(opts);
523
530
  const body = {};
524
531
  if (opts.name)
525
532
  body.name = opts.name;
@@ -531,7 +538,7 @@ program
531
538
  body.totalPrice = opts.price;
532
539
  const data = await fetchJson(`${API_BASE}/api/workflows/${id}`, {
533
540
  method: 'PUT',
534
- headers: walletHeaders(address),
541
+ headers: await walletHeaders(privateKey),
535
542
  body: JSON.stringify(body),
536
543
  });
537
544
  console.log(JSON.stringify(data, null, 2));
@@ -548,10 +555,154 @@ program
548
555
  .option('-k, --key <privateKey>', 'Wallet private key')
549
556
  .action(async (id, opts) => {
550
557
  try {
551
- const { address } = getWallet(opts);
558
+ const { privateKey, address } = getWallet(opts);
552
559
  const data = await fetchJson(`${API_BASE}/api/workflows/${id}`, {
553
560
  method: 'DELETE',
554
- headers: walletHeaders(address),
561
+ headers: await walletHeaders(privateKey),
562
+ });
563
+ console.log(JSON.stringify(data, null, 2));
564
+ }
565
+ catch (err) {
566
+ console.error(`Error: ${err.message}`);
567
+ process.exit(1);
568
+ }
569
+ });
570
+ // ─── my-workflows (creator endpoint view) ───
571
+ program
572
+ .command('my-workflows')
573
+ .description('List workflows you created (uses your wallet)')
574
+ .option('-l, --limit <n>', 'Max results', '50')
575
+ .option('-s, --status <status>', 'Filter: active | draft | paused | all', 'all')
576
+ .option('-k, --key <privateKey>', 'Wallet private key')
577
+ .action(async (opts) => {
578
+ try {
579
+ const { privateKey, address } = getWallet(opts);
580
+ const params = new URLSearchParams({
581
+ limit: opts.limit,
582
+ status: opts.status,
583
+ creator: address,
584
+ });
585
+ const data = await fetchJson(`${API_BASE}/api/workflows?${params}`);
586
+ const workflows = data.workflows || [];
587
+ if (!workflows.length) {
588
+ console.log('No workflows found for this wallet.');
589
+ return;
590
+ }
591
+ const rows = [['ID', 'NAME', 'STATUS', 'VER', 'NODES', 'COST']];
592
+ for (const w of workflows) {
593
+ rows.push([
594
+ w.workflowId,
595
+ truncate(w.name || '', 24),
596
+ w.status,
597
+ `v${w.currentVersion ?? 1}`,
598
+ String(w.graph?.nodes?.length || 0),
599
+ `$${w.totalPrice}`,
600
+ ]);
601
+ }
602
+ printTable(rows);
603
+ }
604
+ catch (err) {
605
+ console.error(`Error: ${err.message}`);
606
+ process.exit(1);
607
+ }
608
+ });
609
+ // ─── workflow-versions ───
610
+ program
611
+ .command('workflow-versions <id>')
612
+ .description('List all versions of a workflow')
613
+ .action(async (id) => {
614
+ try {
615
+ const data = await fetchJson(`${API_BASE}/api/workflows/${id}/versions`);
616
+ const versions = data.versions || [];
617
+ if (!versions.length) {
618
+ console.log('No versions found.');
619
+ return;
620
+ }
621
+ const rows = [['VER', 'DEFAULT', 'VERSION_ID', 'PRICE', 'CREATED', 'CHANGELOG']];
622
+ for (const v of versions) {
623
+ rows.push([
624
+ `v${v.version}`,
625
+ v.isDefault ? '★' : '',
626
+ v.versionId,
627
+ `$${v.totalPrice}`,
628
+ new Date(v.createdAt).toLocaleDateString(),
629
+ truncate(v.changelog || '', 40),
630
+ ]);
631
+ }
632
+ printTable(rows);
633
+ }
634
+ catch (err) {
635
+ console.error(`Error: ${err.message}`);
636
+ process.exit(1);
637
+ }
638
+ });
639
+ // ─── workflow-version-create ───
640
+ program
641
+ .command('workflow-version-create <id>')
642
+ .description('Create a new version of a workflow (graph/price/splits)')
643
+ .option('--graph <json>', 'Graph JSON (inherits current if omitted)')
644
+ .option('--price <usdc>', 'Total price')
645
+ .option('--splits <json>', 'Splits JSON array')
646
+ .option('--changelog <msg>', 'Changelog message')
647
+ .option('--no-default', 'Create as non-default (keeps current version active)')
648
+ .option('-k, --key <privateKey>', 'Wallet private key')
649
+ .action(async (id, opts) => {
650
+ try {
651
+ const { privateKey, address } = getWallet(opts);
652
+ const body = {};
653
+ if (opts.graph)
654
+ body.graph = JSON.parse(opts.graph);
655
+ if (opts.price)
656
+ body.totalPrice = opts.price;
657
+ if (opts.splits)
658
+ body.splits = JSON.parse(opts.splits);
659
+ if (opts.changelog)
660
+ body.changelog = opts.changelog;
661
+ if (opts.default === false)
662
+ body.setDefault = false;
663
+ const data = await fetchJson(`${API_BASE}/api/workflows/${id}/versions`, {
664
+ method: 'POST',
665
+ headers: await walletHeaders(privateKey),
666
+ body: JSON.stringify(body),
667
+ });
668
+ console.log(JSON.stringify(data, null, 2));
669
+ }
670
+ catch (err) {
671
+ console.error(`Error: ${err.message}`);
672
+ process.exit(1);
673
+ }
674
+ });
675
+ // ─── workflow-version-set-default ───
676
+ program
677
+ .command('workflow-version-set-default <id> <versionIdOrNumber>')
678
+ .description('Set a version as the active/default for a workflow (rollback/promote)')
679
+ .option('-k, --key <privateKey>', 'Wallet private key')
680
+ .action(async (id, vid, opts) => {
681
+ try {
682
+ const { privateKey, address } = getWallet(opts);
683
+ const data = await fetchJson(`${API_BASE}/api/workflows/${id}/versions/${vid}`, {
684
+ method: 'PUT',
685
+ headers: await walletHeaders(privateKey),
686
+ body: JSON.stringify({ setDefault: true }),
687
+ });
688
+ console.log(JSON.stringify(data, null, 2));
689
+ }
690
+ catch (err) {
691
+ console.error(`Error: ${err.message}`);
692
+ process.exit(1);
693
+ }
694
+ });
695
+ // ─── workflow-version-delete ───
696
+ program
697
+ .command('workflow-version-delete <id> <versionIdOrNumber>')
698
+ .description('Delete a non-default workflow version')
699
+ .option('-k, --key <privateKey>', 'Wallet private key')
700
+ .action(async (id, vid, opts) => {
701
+ try {
702
+ const { privateKey, address } = getWallet(opts);
703
+ const data = await fetchJson(`${API_BASE}/api/workflows/${id}/versions/${vid}`, {
704
+ method: 'DELETE',
705
+ headers: await walletHeaders(privateKey),
555
706
  });
556
707
  console.log(JSON.stringify(data, null, 2));
557
708
  }
@@ -573,7 +724,7 @@ program
573
724
  if (!endpoint.includes('/'))
574
725
  throw new Error('Format: namespace/endpoint-name');
575
726
  const [ns, ep] = endpoint.split('/');
576
- const { address } = getWallet(opts);
727
+ const { privateKey, address } = getWallet(opts);
577
728
  const details = await fetchJson(`${API_BASE}/api/endpoints/${ns}/${ep}/details`);
578
729
  const body = {
579
730
  endpointName: ep,
@@ -693,7 +844,7 @@ program
693
844
  .option('-k, --key <privateKey>', 'Wallet private key')
694
845
  .action(async (id, opts) => {
695
846
  try {
696
- const { address } = getWallet(opts);
847
+ const { privateKey, address } = getWallet(opts);
697
848
  const data = await fetchJson(`${API_BASE}/api/ai/generate-ui`, {
698
849
  method: 'DELETE',
699
850
  headers: { 'Content-Type': 'application/json' },
@@ -713,7 +864,7 @@ program
713
864
  .option('-k, --key <privateKey>', 'Wallet private key')
714
865
  .action(async (id, opts) => {
715
866
  try {
716
- const { address } = getWallet(opts);
867
+ const { privateKey, address } = getWallet(opts);
717
868
  const data = await fetchJson(`${API_BASE}/api/ai/generate-ui`, {
718
869
  method: 'PATCH',
719
870
  headers: { 'Content-Type': 'application/json' },
@@ -732,7 +883,7 @@ program
732
883
  .option('-k, --key <privateKey>', 'Wallet private key')
733
884
  .action(async (id, opts) => {
734
885
  try {
735
- const { address } = getWallet(opts);
886
+ const { privateKey, address } = getWallet(opts);
736
887
  const data = await fetchJson(`${API_BASE}/api/ai/generate-ui`, {
737
888
  method: 'PATCH',
738
889
  headers: { 'Content-Type': 'application/json' },
@@ -753,7 +904,7 @@ program
753
904
  .option('-k, --key <privateKey>', 'Wallet private key')
754
905
  .action(async (id, opts) => {
755
906
  try {
756
- const { address } = getWallet(opts);
907
+ const { privateKey, address } = getWallet(opts);
757
908
  const data = await fetchJson(`${API_BASE}/api/ai/generate-ui`, {
758
909
  method: 'PATCH',
759
910
  headers: { 'Content-Type': 'application/json' },
@@ -773,7 +924,7 @@ program
773
924
  .option('-k, --key <privateKey>', 'Wallet private key')
774
925
  .action(async (opts) => {
775
926
  try {
776
- const { address } = getWallet(opts);
927
+ const { privateKey, address } = getWallet(opts);
777
928
  const params = new URLSearchParams({ checkFree: '1', wallet: address });
778
929
  const data = await fetchJson(`${API_BASE}/api/ai/generate-ui?${params}`);
779
930
  console.log(`\n Wallet: ${address}`);
@@ -792,9 +943,9 @@ program
792
943
  .option('-k, --key <privateKey>', 'Wallet private key')
793
944
  .action(async (opts) => {
794
945
  try {
795
- const { address } = getWallet(opts);
946
+ const { privateKey, address } = getWallet(opts);
796
947
  const data = await fetchJson(`${API_BASE}/api/user-factory/check-balance`, {
797
- headers: walletHeaders(address),
948
+ headers: await walletHeaders(privateKey),
798
949
  });
799
950
  console.log(`\n Wallet: ${address}`);
800
951
  console.log(` Balance: ${data.balance} $APINOW`);
@@ -813,9 +964,9 @@ program
813
964
  .option('-k, --key <privateKey>', 'Wallet private key')
814
965
  .action(async (opts) => {
815
966
  try {
816
- const { address } = getWallet(opts);
967
+ const { privateKey, address } = getWallet(opts);
817
968
  const data = await fetchJson(`${API_BASE}/api/user-factory`, {
818
- headers: walletHeaders(address),
969
+ headers: await walletHeaders(privateKey),
819
970
  });
820
971
  const endpoints = data.endpoints || [];
821
972
  if (!endpoints.length) {
@@ -845,11 +996,11 @@ program
845
996
  .option('-k, --key <privateKey>', 'Wallet private key')
846
997
  .action(async (idea, opts) => {
847
998
  try {
848
- const { address } = getWallet(opts);
999
+ const { privateKey, address } = getWallet(opts);
849
1000
  console.error('Generating endpoint config…');
850
1001
  const data = await fetchJson(`${API_BASE}/api/user-factory/generate`, {
851
1002
  method: 'POST',
852
- headers: walletHeaders(address),
1003
+ headers: await walletHeaders(privateKey),
853
1004
  body: JSON.stringify({ idea }),
854
1005
  });
855
1006
  console.log(JSON.stringify(data, null, 2));
@@ -876,7 +1027,7 @@ program
876
1027
  .option('-k, --key <privateKey>', 'Wallet private key')
877
1028
  .action(async (opts) => {
878
1029
  try {
879
- const { address } = getWallet(opts);
1030
+ const { privateKey, address } = getWallet(opts);
880
1031
  let body;
881
1032
  if (opts.fromJson) {
882
1033
  const { readFileSync } = await import('fs');
@@ -933,7 +1084,7 @@ program
933
1084
  console.error(`Creating endpoint "${body.name}"…`);
934
1085
  const data = await fetchJson(`${API_BASE}/api/user-factory`, {
935
1086
  method: 'POST',
936
- headers: walletHeaders(address),
1087
+ headers: await walletHeaders(privateKey),
937
1088
  body: JSON.stringify(body),
938
1089
  });
939
1090
  const ep = data.endpoint;
@@ -961,7 +1112,7 @@ program
961
1112
  .option('-k, --key <privateKey>', 'Wallet private key')
962
1113
  .action(async (endpointId, opts) => {
963
1114
  try {
964
- const { address } = getWallet(opts);
1115
+ const { privateKey, address } = getWallet(opts);
965
1116
  const body = { endpointId, markupPercent: Number(opts.markup) };
966
1117
  if (opts.name)
967
1118
  body.workflowName = opts.name;
@@ -977,7 +1128,7 @@ program
977
1128
  console.error(`Creating markup workflow (${opts.markup}%${tbLabel})…`);
978
1129
  const data = await fetchJson(`${API_BASE}/api/user-factory/markup`, {
979
1130
  method: 'POST',
980
- headers: walletHeaders(address),
1131
+ headers: await walletHeaders(privateKey),
981
1132
  body: JSON.stringify(body),
982
1133
  });
983
1134
  const w = data.workflow;
@@ -1006,7 +1157,7 @@ program
1006
1157
  try {
1007
1158
  if (!endpoint.includes('/'))
1008
1159
  throw new Error('Format: namespace/endpoint-name');
1009
- const { address } = getWallet(opts);
1160
+ const { privateKey, address } = getWallet(opts);
1010
1161
  const [namespace, endpointName] = endpoint.split('/');
1011
1162
  const body = { namespace, endpointName };
1012
1163
  if (opts.data)
@@ -1016,7 +1167,7 @@ program
1016
1167
  console.error(`Testing ${endpoint}…`);
1017
1168
  const data = await fetchJson(`${API_BASE}/api/user-factory/test-call`, {
1018
1169
  method: 'POST',
1019
- headers: walletHeaders(address),
1170
+ headers: await walletHeaders(privateKey),
1020
1171
  body: JSON.stringify(body),
1021
1172
  });
1022
1173
  console.log(JSON.stringify(data.output, null, 2));
@@ -1044,12 +1195,12 @@ program
1044
1195
  .option('-k, --key <privateKey>', 'Wallet private key')
1045
1196
  .action(async (idea, opts) => {
1046
1197
  try {
1047
- const { address } = getWallet(opts);
1198
+ const { privateKey, address } = getWallet(opts);
1048
1199
  // Step 1: Generate
1049
1200
  console.error('Step 1/4: Generating endpoint config from idea…');
1050
1201
  const draft = await fetchJson(`${API_BASE}/api/user-factory/generate`, {
1051
1202
  method: 'POST',
1052
- headers: walletHeaders(address),
1203
+ headers: await walletHeaders(privateKey),
1053
1204
  body: JSON.stringify({ idea }),
1054
1205
  });
1055
1206
  console.error(` → ${draft.name}: ${draft.description}`);
@@ -1075,7 +1226,7 @@ program
1075
1226
  createBody.recipientWallet = opts.recipient;
1076
1227
  const createData = await fetchJson(`${API_BASE}/api/user-factory`, {
1077
1228
  method: 'POST',
1078
- headers: walletHeaders(address),
1229
+ headers: await walletHeaders(privateKey),
1079
1230
  body: JSON.stringify(createBody),
1080
1231
  });
1081
1232
  const ep = createData.endpoint;
@@ -1085,7 +1236,7 @@ program
1085
1236
  const testInput = draft.exampleInput || { prompt: 'test' };
1086
1237
  const testData = await fetchJson(`${API_BASE}/api/user-factory/test-call`, {
1087
1238
  method: 'POST',
1088
- headers: walletHeaders(address),
1239
+ headers: await walletHeaders(privateKey),
1089
1240
  body: JSON.stringify({
1090
1241
  namespace: ep.namespace,
1091
1242
  endpointName: ep.endpointName,
@@ -1112,7 +1263,7 @@ program
1112
1263
  markupBody.tokenBuyCA = opts.tokenBuyCa;
1113
1264
  const markupData = await fetchJson(`${API_BASE}/api/user-factory/markup`, {
1114
1265
  method: 'POST',
1115
- headers: walletHeaders(address),
1266
+ headers: await walletHeaders(privateKey),
1116
1267
  body: JSON.stringify(markupBody),
1117
1268
  });
1118
1269
  workflow = markupData.workflow;
package/dist/index.d.ts CHANGED
@@ -55,6 +55,11 @@ export interface GeneratedUI {
55
55
  }
56
56
  export declare function createClient(config: ApinowConfig): {
57
57
  wallet: `0x${string}`;
58
+ /**
59
+ * Produce a signed `Authorization` header for custom write calls.
60
+ * Pairs with `x-wallet-address`. Backend accepts msg within ~10 min.
61
+ */
62
+ signAuthHeader: () => Promise<Record<string, string>>;
58
63
  /**
59
64
  * Call any APINow endpoint. Handles x402 payment automatically.
60
65
  *
@@ -138,6 +143,52 @@ export declare function createClient(config: ApinowConfig): {
138
143
  }): Promise<any>;
139
144
  updateWorkflow(workflowId: string, updates: Record<string, any>): Promise<any>;
140
145
  deleteWorkflow(workflowId: string): Promise<any>;
146
+ /**
147
+ * List workflows you created (convenience for `listWorkflows({ creator: yourWallet })`).
148
+ */
149
+ listMyWorkflows(opts?: {
150
+ status?: string;
151
+ limit?: number;
152
+ }): Promise<any>;
153
+ /**
154
+ * List all versions of a workflow (public, free).
155
+ */
156
+ listWorkflowVersions(workflowId: string): Promise<{
157
+ versions: any[];
158
+ }>;
159
+ /**
160
+ * Get a specific workflow version by versionId or numeric version.
161
+ */
162
+ getWorkflowVersion(workflowId: string, versionIdOrNumber: string | number): Promise<any>;
163
+ /**
164
+ * Create a new workflow version (creator only). Defaults to setting it as default.
165
+ * Omit fields to inherit from current workflow.
166
+ */
167
+ createWorkflowVersion(workflowId: string, updates?: {
168
+ graph?: {
169
+ nodes: any[];
170
+ outputNode: string;
171
+ outputMapping?: any;
172
+ };
173
+ totalPrice?: string;
174
+ splits?: Array<{
175
+ address: string;
176
+ basisPoints: number;
177
+ label?: string;
178
+ tokenAddress?: string;
179
+ }>;
180
+ mermaidDiagram?: string;
181
+ executionMode?: "balanced" | "optimistic" | "settle_first";
182
+ changelog?: string;
183
+ setDefault?: boolean;
184
+ forkedFrom?: string;
185
+ }): Promise<any>;
186
+ /**
187
+ * Set a version as the default (active) for a workflow. Also rolls the
188
+ * workflow's graph/price/splits back to that version's snapshot.
189
+ */
190
+ setDefaultWorkflowVersion(workflowId: string, versionIdOrNumber: string | number): Promise<any>;
191
+ deleteWorkflowVersion(workflowId: string, versionIdOrNumber: string | number): Promise<any>;
141
192
  /**
142
193
  * Run a workflow. Handles x402 payment automatically.
143
194
  *
package/dist/index.js CHANGED
@@ -33,8 +33,54 @@ export function createClient(config) {
33
33
  const res = await rawFetchWithPayment(input, init);
34
34
  return followRedirects(res);
35
35
  });
36
+ /**
37
+ * Produce an `Authorization: Bearer <msg>||<sig>||<addr>` header signed by
38
+ * the wallet's private key. Backend verifies with ethers.recoverAddress and
39
+ * rejects messages older than ~10 min.
40
+ *
41
+ * Exposed as a public helper so agents can sign custom write calls too.
42
+ */
43
+ async function signAuthHeader() {
44
+ const issuedAt = new Date().toISOString();
45
+ const nonce = Math.random().toString(36).slice(2) + Date.now().toString(36);
46
+ const message = `APINow auth\naddress: ${account.address}\nissuedAt: ${issuedAt}\nnonce: ${nonce}`;
47
+ const signature = await account.signMessage({ message });
48
+ return {
49
+ Authorization: `Bearer ${message}||${signature}||${account.address}`,
50
+ 'x-wallet-address': account.address,
51
+ };
52
+ }
53
+ /**
54
+ * Fetch wrapper that signs write requests with the wallet private key.
55
+ * Use for any non-paid mutating API (endpoint/workflow/version CRUD).
56
+ */
57
+ async function authedFetch(url, init = {}) {
58
+ const authHeaders = await signAuthHeader();
59
+ const res = await fetch(url, {
60
+ ...init,
61
+ headers: {
62
+ ...(init.body ? { 'Content-Type': 'application/json' } : {}),
63
+ ...authHeaders,
64
+ ...init.headers,
65
+ },
66
+ });
67
+ return res;
68
+ }
69
+ async function authedJson(url, init = {}) {
70
+ const res = await authedFetch(url, init);
71
+ if (!res.ok) {
72
+ const text = await res.text();
73
+ throw new Error(`APINow ${res.status}: ${text}`);
74
+ }
75
+ return res.json();
76
+ }
36
77
  return {
37
78
  wallet: account.address,
79
+ /**
80
+ * Produce a signed `Authorization` header for custom write calls.
81
+ * Pairs with `x-wallet-address`. Backend accepts msg within ~10 min.
82
+ */
83
+ signAuthHeader,
38
84
  /**
39
85
  * Call any APINow endpoint. Handles x402 payment automatically.
40
86
  *
@@ -84,16 +130,10 @@ export function createClient(config) {
84
130
  },
85
131
  // ─── Endpoint CRUD ───
86
132
  async createEndpoint(config) {
87
- const res = await fetch(`${baseUrl}/api/endpoints`, {
133
+ return authedJson(`${baseUrl}/api/endpoints`, {
88
134
  method: 'POST',
89
- headers: { 'Content-Type': 'application/json', 'x-wallet-address': account.address },
90
135
  body: JSON.stringify(config),
91
136
  });
92
- if (!res.ok) {
93
- const text = await res.text();
94
- throw new Error(`Failed to create endpoint: ${res.status} ${text}`);
95
- }
96
- return res.json();
97
137
  },
98
138
  async getEndpoint(id) {
99
139
  const res = await fetch(`${baseUrl}/api/endpoints/${id}`);
@@ -102,27 +142,13 @@ export function createClient(config) {
102
142
  return res.json();
103
143
  },
104
144
  async updateEndpoint(id, updates) {
105
- const res = await fetch(`${baseUrl}/api/endpoints/${id}`, {
145
+ return authedJson(`${baseUrl}/api/endpoints/${id}`, {
106
146
  method: 'PUT',
107
- headers: { 'Content-Type': 'application/json', 'x-wallet-address': account.address },
108
147
  body: JSON.stringify(updates),
109
148
  });
110
- if (!res.ok) {
111
- const text = await res.text();
112
- throw new Error(`Failed to update endpoint: ${res.status} ${text}`);
113
- }
114
- return res.json();
115
149
  },
116
150
  async deleteEndpoint(id) {
117
- const res = await fetch(`${baseUrl}/api/endpoints/${id}`, {
118
- method: 'DELETE',
119
- headers: { 'x-wallet-address': account.address },
120
- });
121
- if (!res.ok) {
122
- const text = await res.text();
123
- throw new Error(`Failed to delete endpoint: ${res.status} ${text}`);
124
- }
125
- return res.json();
151
+ return authedJson(`${baseUrl}/api/endpoints/${id}`, { method: 'DELETE' });
126
152
  },
127
153
  async listEndpoints(opts = {}) {
128
154
  const params = new URLSearchParams();
@@ -166,40 +192,68 @@ export function createClient(config) {
166
192
  return res.json();
167
193
  },
168
194
  async createWorkflow(config) {
169
- const res = await fetch(`${baseUrl}/api/workflows`, {
195
+ return authedJson(`${baseUrl}/api/workflows`, {
170
196
  method: 'POST',
171
- headers: { 'Content-Type': 'application/json', 'x-wallet-address': account.address },
172
197
  body: JSON.stringify(config),
173
198
  });
174
- if (!res.ok) {
175
- const text = await res.text();
176
- throw new Error(`Failed to create workflow: ${res.status} ${text}`);
177
- }
178
- return res.json();
179
199
  },
180
200
  async updateWorkflow(workflowId, updates) {
181
- const res = await fetch(`${baseUrl}/api/workflows/${workflowId}`, {
201
+ return authedJson(`${baseUrl}/api/workflows/${workflowId}`, {
182
202
  method: 'PUT',
183
- headers: { 'Content-Type': 'application/json', 'x-wallet-address': account.address },
184
203
  body: JSON.stringify(updates),
185
204
  });
186
- if (!res.ok) {
187
- const text = await res.text();
188
- throw new Error(`Failed to update workflow: ${res.status} ${text}`);
189
- }
190
- return res.json();
191
205
  },
192
206
  async deleteWorkflow(workflowId) {
193
- const res = await fetch(`${baseUrl}/api/workflows/${workflowId}`, {
194
- method: 'DELETE',
195
- headers: { 'x-wallet-address': account.address },
196
- });
197
- if (!res.ok) {
198
- const text = await res.text();
199
- throw new Error(`Failed to delete workflow: ${res.status} ${text}`);
200
- }
207
+ return authedJson(`${baseUrl}/api/workflows/${workflowId}`, { method: 'DELETE' });
208
+ },
209
+ /**
210
+ * List workflows you created (convenience for `listWorkflows({ creator: yourWallet })`).
211
+ */
212
+ async listMyWorkflows(opts = {}) {
213
+ return this.listWorkflows({ ...opts, creator: account.address });
214
+ },
215
+ // ─── Workflow Versions ───
216
+ /**
217
+ * List all versions of a workflow (public, free).
218
+ */
219
+ async listWorkflowVersions(workflowId) {
220
+ const res = await fetch(`${baseUrl}/api/workflows/${workflowId}/versions`);
221
+ if (!res.ok)
222
+ throw new Error(`Failed to list versions: ${res.status}`);
223
+ return res.json();
224
+ },
225
+ /**
226
+ * Get a specific workflow version by versionId or numeric version.
227
+ */
228
+ async getWorkflowVersion(workflowId, versionIdOrNumber) {
229
+ const res = await fetch(`${baseUrl}/api/workflows/${workflowId}/versions/${versionIdOrNumber}`);
230
+ if (!res.ok)
231
+ throw new Error(`Failed to get version: ${res.status}`);
201
232
  return res.json();
202
233
  },
234
+ /**
235
+ * Create a new workflow version (creator only). Defaults to setting it as default.
236
+ * Omit fields to inherit from current workflow.
237
+ */
238
+ async createWorkflowVersion(workflowId, updates = {}) {
239
+ return authedJson(`${baseUrl}/api/workflows/${workflowId}/versions`, {
240
+ method: 'POST',
241
+ body: JSON.stringify(updates),
242
+ });
243
+ },
244
+ /**
245
+ * Set a version as the default (active) for a workflow. Also rolls the
246
+ * workflow's graph/price/splits back to that version's snapshot.
247
+ */
248
+ async setDefaultWorkflowVersion(workflowId, versionIdOrNumber) {
249
+ return authedJson(`${baseUrl}/api/workflows/${workflowId}/versions/${versionIdOrNumber}`, {
250
+ method: 'PUT',
251
+ body: JSON.stringify({ setDefault: true }),
252
+ });
253
+ },
254
+ async deleteWorkflowVersion(workflowId, versionIdOrNumber) {
255
+ return authedJson(`${baseUrl}/api/workflows/${workflowId}/versions/${versionIdOrNumber}`, { method: 'DELETE' });
256
+ },
203
257
  /**
204
258
  * Run a workflow. Handles x402 payment automatically.
205
259
  *
@@ -217,82 +271,50 @@ export function createClient(config) {
217
271
  * Check $APINOW token balance and factory access.
218
272
  */
219
273
  async factoryBalance() {
220
- const res = await fetch(`${baseUrl}/api/user-factory/check-balance`, {
221
- headers: { 'x-wallet-address': account.address },
222
- });
223
- if (!res.ok)
224
- throw new Error(`Failed to check balance: ${res.status}`);
225
- return res.json();
274
+ return authedJson(`${baseUrl}/api/user-factory/check-balance`);
226
275
  },
227
276
  /**
228
277
  * List your user-factory endpoints.
229
278
  */
230
279
  async factoryList() {
231
- const res = await fetch(`${baseUrl}/api/user-factory`, {
232
- headers: { 'x-wallet-address': account.address },
233
- });
234
- if (!res.ok)
235
- throw new Error(`Failed to list factory endpoints: ${res.status}`);
236
- return res.json();
280
+ return authedJson(`${baseUrl}/api/user-factory`);
237
281
  },
238
282
  /**
239
283
  * Generate endpoint config from a natural-language idea.
240
284
  */
241
285
  async factoryGenerate(idea) {
242
- const res = await fetch(`${baseUrl}/api/user-factory/generate`, {
286
+ return authedJson(`${baseUrl}/api/user-factory/generate`, {
243
287
  method: 'POST',
244
- headers: { 'Content-Type': 'application/json', 'x-wallet-address': account.address },
245
288
  body: JSON.stringify({ idea }),
246
289
  });
247
- if (!res.ok)
248
- throw new Error(`Failed to generate: ${res.status}`);
249
- return res.json();
250
290
  },
251
291
  /**
252
292
  * Create an LLM endpoint via user-factory.
253
293
  */
254
294
  async factoryCreate(config) {
255
- const res = await fetch(`${baseUrl}/api/user-factory`, {
295
+ return authedJson(`${baseUrl}/api/user-factory`, {
256
296
  method: 'POST',
257
- headers: { 'Content-Type': 'application/json', 'x-wallet-address': account.address },
258
297
  body: JSON.stringify(config),
259
298
  });
260
- if (!res.ok) {
261
- const text = await res.text();
262
- throw new Error(`Failed to create endpoint: ${res.status} ${text}`);
263
- }
264
- return res.json();
265
299
  },
266
300
  /**
267
301
  * Create a markup workflow wrapping an existing endpoint.
268
302
  * Optionally allocate a portion of the markup to a token buy split.
269
303
  */
270
304
  async factoryMarkup(opts) {
271
- const res = await fetch(`${baseUrl}/api/user-factory/markup`, {
305
+ return authedJson(`${baseUrl}/api/user-factory/markup`, {
272
306
  method: 'POST',
273
- headers: { 'Content-Type': 'application/json', 'x-wallet-address': account.address },
274
307
  body: JSON.stringify(opts),
275
308
  });
276
- if (!res.ok) {
277
- const text = await res.text();
278
- throw new Error(`Failed to create markup: ${res.status} ${text}`);
279
- }
280
- return res.json();
281
309
  },
282
310
  /**
283
311
  * Test-call an endpoint without payment (free, server-side LLM call).
284
312
  */
285
313
  async factoryTestCall(opts) {
286
- const res = await fetch(`${baseUrl}/api/user-factory/test-call`, {
314
+ return authedJson(`${baseUrl}/api/user-factory/test-call`, {
287
315
  method: 'POST',
288
- headers: { 'Content-Type': 'application/json', 'x-wallet-address': account.address },
289
316
  body: JSON.stringify(opts),
290
317
  });
291
- if (!res.ok) {
292
- const text = await res.text();
293
- throw new Error(`Failed to test call: ${res.status} ${text}`);
294
- }
295
- return res.json();
296
318
  },
297
319
  // ─── Factory Pipeline ───
298
320
  /**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "apinow-sdk",
3
- "version": "0.24.0",
3
+ "version": "0.26.0",
4
4
  "description": "Pay-per-call API SDK & CLI for APINow.fun — endpoints + workflows, wraps x402 so you don't have to",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",