spaps-sdk 1.9.0 → 1.10.1

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/CHANGELOG.md CHANGED
@@ -6,6 +6,18 @@ The format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/) and
6
6
 
7
7
  ## [Unreleased]
8
8
 
9
+ ### Added
10
+
11
+ - Added issue-reporting screenshot attachment helpers for private upload, list, access URL, delete, and attach-by-ID create/update/reply flows.
12
+
13
+ ## [1.10.0] - 2026-05-11
14
+
15
+ - Added usage-control SDK support for authorizing usage and recording immutable usage events against SPAPS.
16
+
17
+ ## [1.9.0] - 2026-05-11
18
+
19
+ - Maintenance: align the SDK package version after release automation.
20
+
9
21
  ## [1.8.0] - 2026-05-11
10
22
 
11
23
  - Maintenance: align the SDK package version after release automation.
package/README.md CHANGED
@@ -23,7 +23,7 @@ This package targets `Node.js >=14`.
23
23
 
24
24
  | Need | Package gives you |
25
25
  | --- | --- |
26
- | One client for many SPAPS surfaces | `auth`, `payments`, `sessions`, `secureMessages`, `issueReporting`, `appLinks`, `email`, `entitlements`, `skillEvals`, `dayrate`, `admin`, and `cfo` namespaces |
26
+ | One client for many SPAPS surfaces | `auth`, `payments`, `sessions`, `secureMessages`, `issueReporting`, `appLinks`, `email`, `entitlements`, `usage`, `skillEvals`, `dayrate`, `admin`, and `cfo` namespaces |
27
27
  | Local development without extra config | Localhost URLs automatically enable local mode |
28
28
  | Browser and server usage | `publishableKey`, `secretKey`, or legacy `apiKey` support |
29
29
  | Shared contracts | Re-exports a large slice of `spaps-types` |
@@ -84,10 +84,11 @@ Relevant environment variables:
84
84
  | `payments` | Checkout sessions, products, prices, subscriptions, and crypto helpers |
85
85
  | `sessions` | Session lookup, validation, and lifecycle helpers |
86
86
  | `secureMessages` | Secure-message create/list helpers |
87
- | `issueReporting` | Status, history, create, update, reply, and voice-token flows |
87
+ | `issueReporting` | Status, history, create, update, reply, voice-token, and private screenshot attachment flows |
88
88
  | `appLinks` | Authenticated create and public resolve helpers for application-scoped short links |
89
89
  | `email` | Template lookup, preview, and send helpers |
90
90
  | `entitlements` | User and resource entitlement queries |
91
+ | `usage` | Secret-key usage authorization and immutable usage recording |
91
92
  | `skillEvals` | Paid blind skill-eval cases, review rooms, reviewer marks, insight inboxes, and controlled reveal |
92
93
  | `dayrate` | Availability, Stripe booking, x402 booking-hold, and checkout-status helpers |
93
94
  | `admin` | Product and pricing admin helpers |
@@ -123,6 +124,37 @@ const voiceToken = await spaps.issueReporting.createVoiceToken();
123
124
  console.log(voiceToken.provider, voiceToken.model_id);
124
125
  ```
125
126
 
127
+ ### Issue Reporting Screenshot Attachments
128
+
129
+ Screenshots are uploaded as private pending hosted assets first. Create, update, or reply calls then send only attachment IDs; do not put raw image bytes, data URLs, or base64 payloads in issue notes or target metadata.
130
+
131
+ ```ts
132
+ spaps.setAccessToken(accessToken);
133
+
134
+ const file = new File([pngBytes], "protocol-save-failure.png", {
135
+ type: "image/png",
136
+ });
137
+ const attachment = await spaps.issueReporting.uploadAttachment(file);
138
+
139
+ const issue = await spaps.issueReporting.create({
140
+ target: {
141
+ component_key: "patient_protocol_widget",
142
+ component_label: "Patient Protocol Widget",
143
+ page_url: "/patients/123/protocol",
144
+ surface_ref: "daily-log",
145
+ metadata: { section: "daily log" },
146
+ },
147
+ note: "The save action silently fails after I edit today's protocol note.",
148
+ reporter_role_hint: "practitioner",
149
+ attachment_ids: [attachment.id],
150
+ });
151
+
152
+ const access = await spaps.issueReporting.getAttachmentAccessUrl(attachment.id);
153
+ console.log(issue.id, access.expires_in_seconds);
154
+ ```
155
+
156
+ SPAPS accepts PNG, JPEG, and WebP screenshots up to 10 MiB each, with at most 5 retained screenshots per report. The hosted object remains private; callers fetch a short-lived access URL after normal issue-reporting authorization succeeds. SPAPS does not redact screenshot contents, so host apps should warn users when a capture may include sensitive data.
157
+
126
158
  ### Application Short Links
127
159
 
128
160
  Use `appLinks` when a browser app needs a stable public URL for large local state, such as compressed diagram state.
@@ -144,6 +176,57 @@ const link = await spaps.appLinks.create({
144
176
  console.log(`/${link.app_slug}/${link.username}/${link.slug}`);
145
177
  ```
146
178
 
179
+ ### Server-Side Usage Control
180
+
181
+ Use `usage` from a trusted backend with a secret key. Browser apps should call
182
+ their own backend first; that backend asks SPAPS to authorize the work, performs
183
+ the work only on an allow or warning decision, then records the actual usage
184
+ with a stable idempotency key.
185
+
186
+ ```ts
187
+ const spaps = new SPAPSClient({
188
+ apiUrl: "https://api.example.test",
189
+ secretKey: process.env.SPAPS_SECRET_KEY,
190
+ });
191
+
192
+ const authorization = await spaps.usage.authorize({
193
+ feature_key: "assistant_tokens",
194
+ resource_type: "company",
195
+ resource_id: "company_123",
196
+ subject_user_id: "user_123",
197
+ dimensions: {
198
+ requests: 1,
199
+ input_tokens: 1200,
200
+ },
201
+ });
202
+
203
+ if (authorization.decision === "blocked") {
204
+ throw new Error(authorization.reasons[0]?.message || "Usage not authorized");
205
+ }
206
+
207
+ const result = await runAssistantJob();
208
+
209
+ await spaps.usage.record({
210
+ authorization_ref: authorization.authorization_ref,
211
+ idempotency_key: result.jobId,
212
+ feature_key: "assistant_tokens",
213
+ resource_type: "company",
214
+ resource_id: "company_123",
215
+ subject_user_id: "user_123",
216
+ dimensions: {
217
+ input_tokens: result.inputTokens,
218
+ output_tokens: result.outputTokens,
219
+ },
220
+ metadata: {
221
+ job_id: result.jobId,
222
+ },
223
+ });
224
+ ```
225
+
226
+ For dashboard and policy views, use `spaps.usage.listFeatures()`,
227
+ `spaps.usage.getStatus(...)`, and `spaps.usage.listHistory(...)` from the same
228
+ trusted backend context.
229
+
147
230
  ### Skill Evals
148
231
 
149
232
  Use `skillEvals` for SPAPS-owned blind review of agent-skill logs. Paid creation uses the same `PAYMENT-SIGNATURE` header shape as the x402 namespace. Reviewers submit `valuable` and `not_valuable` marks, and submitters read those marks through an insight inbox before applying skill changes.
@@ -365,7 +448,7 @@ npm run test:readme
365
448
  ## Metadata
366
449
 
367
450
  - `package_name`: `spaps-sdk`
368
- - `latest_version`: `1.8.0`
451
+ - `latest_version`: `1.10.0`
369
452
  - `minimum_runtime`: `Node.js >=14.0.0`
370
453
  - `api_base_url`: `https://api.sweetpotato.dev`
371
454
 
package/dist/index.d.mts CHANGED
@@ -1,6 +1,6 @@
1
1
  import * as spaps_types from 'spaps-types';
2
- import { ResourceType, Entitlement, CreateProductRequest, Product, UpdateProductRequest, CreatePriceRequest, Price, ProductSyncResult, CryptoReconcileRequest, CreateSecureMessageRequest, SecureMessage, IssueReportScope, IssueReportStatusResult, IssueReportStatus, IssueReportListResult, IssueReport, CreateIssueReportRequest, IssueReportingVoiceTokenResult, UpdateIssueReportRequest, ReplyIssueReportRequest, CreateAppLinkRequest, AppLink, AuthResponse, User as User$1, CreateCryptoInvoiceRequest, CryptoInvoiceStatusSnapshot, CheckoutSession, X402ResourceStatusResponse, X402ActionResponse, X402ReceiptResponse, X402ReceiptListResponse, X402HandoffVerification, DayrateAvailabilityResponse, DayrateBookingRequest, DayrateBookingResponse, DayrateMultiBookingRequest, DayrateMultiBookingResponse, DayrateX402BookingRequest, DayrateX402BookingResponse, DayrateCheckoutStatusResponse, Subscription, UsageBalance, VerifyCryptoWebhookSignatureOptions } from 'spaps-types';
3
- export { AdminPermission, AdminRole, AdminUser, ApiResponse, AppLink, AuthResponse, CheckoutSession, CreateAppLinkRequest, CreateCryptoInvoiceRequest, CreateIssueReportRequest, CreatePriceRequest, CreateProductRequest, CreateSecureMessageInput, CreateSecureMessageRequest, CryptoInvoice, CryptoInvoiceResponse, CryptoInvoiceStatusSnapshot, CryptoReconcileRequest, DayrateAvailabilityResponse, DayrateAvailableSlot, DayrateBookingRequest, DayrateBookingResponse, DayrateCheckoutStatus, DayrateCheckoutStatusBooking, DayrateCheckoutStatusResponse, DayrateDayOfWeek, DayrateMultiBookingRequest, DayrateMultiBookingResponse, DayratePriceBreakdown, DayrateSlotType, DayrateX402BookingRequest, DayrateX402BookingResponse, Entitlement, IssueReport, IssueReportListResult, IssueReportStatus, IssueReportStatusResult, IssueReportTarget, IssueReportingInputMode, IssueReportingVoiceProvider, IssueReportingVoiceTokenResult, LinkedIssueReportCase, Price, Product, ProductSyncResult, ReplyIssueReportRequest, ResourceType, SecureMessage, SecureMessageOutput, Subscription, TokenPair, UpdateIssueReportRequest, UpdateProductRequest, UsageBalance, User, UserProfile, UserRole, UserWallet, VerifyCryptoWebhookSignatureOptions, X402ActionFreeResponse, X402ActionOutcome, X402ActionPendingResponse, X402ActionReplayedResponse, X402ActionResponse, X402ActionSettledResponse, X402ExecuteActionRequest, X402HandoffAuthorization, X402HandoffVerification, X402HandoffVerifyRequest, X402PaymentAccept, X402PaymentRequirement, X402ProjectionStatus, X402Receipt, X402ReceiptListResponse, X402ReceiptResponse, X402ReceiptStatus, X402Resource, X402ResourceStatus, X402ResourceStatusResponse, atomicToMoneyUnits, createSecureMessageRequestSchema, isX402PaymentRequired, isX402ResourceStatus, moneyUnitsToAtomic, roundHalfToPositiveInfinity, secureMessageMetadataSchema, secureMessageSchema, validatePositiveAmountAtomic } from 'spaps-types';
2
+ import { ResourceType, Entitlement, CreateProductRequest, Product, UpdateProductRequest, CreatePriceRequest, Price, ProductSyncResult, CryptoReconcileRequest, CreateSecureMessageRequest, SecureMessage, IssueReportScope, IssueReportStatusResult, IssueReportStatus, IssueReportListResult, IssueReport, CreateIssueReportRequest, IssueReportAttachmentOut, ListIssueReportAttachmentsResponse, IssueReportAttachmentAccessResponse, IssueReportingVoiceTokenResult, UpdateIssueReportRequest, ReplyIssueReportRequest, CreateAppLinkRequest, AppLink, AuthResponse, User as User$1, CreateCryptoInvoiceRequest, CryptoInvoiceStatusSnapshot, CheckoutSession, X402ResourceStatusResponse, X402ActionResponse, X402ReceiptResponse, X402ReceiptListResponse, X402HandoffVerification, DayrateAvailabilityResponse, DayrateBookingRequest, DayrateBookingResponse, DayrateMultiBookingRequest, DayrateMultiBookingResponse, DayrateX402BookingRequest, DayrateX402BookingResponse, DayrateCheckoutStatusResponse, UsageControlFeaturesResponse, UsageControlStatusRequest, UsageControlStatusResponse, UsageControlAuthorizeRequest, UsageControlAuthorizeResponse, UsageControlRecordRequest, UsageControlRecordResponse, UsageControlHistoryRequest, UsageControlHistoryResponse, Subscription, VerifyCryptoWebhookSignatureOptions } from 'spaps-types';
3
+ export { AdminPermission, AdminRole, AdminUser, ApiResponse, AppLink, AuthResponse, CheckoutSession, CreateAppLinkRequest, CreateCryptoInvoiceRequest, CreateIssueReportRequest, CreatePriceRequest, CreateProductRequest, CreateSecureMessageInput, CreateSecureMessageRequest, CryptoInvoice, CryptoInvoiceResponse, CryptoInvoiceStatusSnapshot, CryptoReconcileRequest, DayrateAvailabilityResponse, DayrateAvailableSlot, DayrateBookingRequest, DayrateBookingResponse, DayrateCheckoutStatus, DayrateCheckoutStatusBooking, DayrateCheckoutStatusResponse, DayrateDayOfWeek, DayrateMultiBookingRequest, DayrateMultiBookingResponse, DayratePriceBreakdown, DayrateSlotType, DayrateX402BookingRequest, DayrateX402BookingResponse, Entitlement, IssueReport, IssueReportAttachmentAccessResponse, IssueReportAttachmentOut, IssueReportListResult, IssueReportStatus, IssueReportStatusResult, IssueReportTarget, IssueReportingInputMode, IssueReportingVoiceProvider, IssueReportingVoiceTokenResult, LinkedIssueReportCase, ListIssueReportAttachmentsResponse, Price, Product, ProductSyncResult, ReplyIssueReportRequest, ResourceType, SecureMessage, SecureMessageOutput, Subscription, TokenPair, UpdateIssueReportRequest, UpdateProductRequest, UsageControlAuthorizeRequest, UsageControlAuthorizeResponse, UsageControlDecision, UsageControlDimensions, UsageControlError, UsageControlErrorCode, UsageControlFeaturesResponse, UsageControlHistoryRequest, UsageControlHistoryResponse, UsageControlLedgerEvent, UsageControlRecordRequest, UsageControlRecordResponse, UsageControlRecordStatus, UsageControlStatusRequest, UsageControlStatusResponse, User, UserProfile, UserRole, UserWallet, VerifyCryptoWebhookSignatureOptions, X402ActionFreeResponse, X402ActionOutcome, X402ActionPendingResponse, X402ActionReplayedResponse, X402ActionResponse, X402ActionSettledResponse, X402ExecuteActionRequest, X402HandoffAuthorization, X402HandoffVerification, X402HandoffVerifyRequest, X402PaymentAccept, X402PaymentRequirement, X402ProjectionStatus, X402Receipt, X402ReceiptListResponse, X402ReceiptResponse, X402ReceiptStatus, X402Resource, X402ResourceStatus, X402ResourceStatusResponse, atomicToMoneyUnits, createSecureMessageRequestSchema, isX402PaymentRequired, isX402ResourceStatus, moneyUnitsToAtomic, roundHalfToPositiveInfinity, secureMessageMetadataSchema, secureMessageSchema, validatePositiveAmountAtomic } from 'spaps-types';
4
4
 
5
5
  /**
6
6
  * Permission checking utilities for SPAPS SDK
@@ -360,6 +360,9 @@ interface IssueReportListParams {
360
360
  interface IssueReportStatusParams {
361
361
  scope?: SupportedIssueReportScope;
362
362
  }
363
+ interface IssueReportAttachmentUploadOptions {
364
+ filename?: string;
365
+ }
363
366
  declare class X402PaymentRequiredSDKError extends Error {
364
367
  readonly paymentRequiredHeader: string;
365
368
  readonly response: unknown;
@@ -730,6 +733,22 @@ declare class SPAPSClient<SecureMessageMetadata extends Record<string, any> = Re
730
733
  * Create a new canonical issue report.
731
734
  */
732
735
  create: (payload: CreateIssueReportRequest) => Promise<IssueReport>;
736
+ /**
737
+ * Upload one private pending screenshot attachment.
738
+ */
739
+ uploadAttachment: (file: Blob, options?: IssueReportAttachmentUploadOptions) => Promise<IssueReportAttachmentOut>;
740
+ /**
741
+ * List non-deleted screenshot attachments for one issue report.
742
+ */
743
+ listAttachments: (issueReportId: string) => Promise<ListIssueReportAttachmentsResponse>;
744
+ /**
745
+ * Return a short-lived private attachment access URL.
746
+ */
747
+ getAttachmentAccessUrl: (attachmentId: string) => Promise<IssueReportAttachmentAccessResponse>;
748
+ /**
749
+ * Soft-delete one owned screenshot attachment.
750
+ */
751
+ deleteAttachment: (attachmentId: string) => Promise<void>;
733
752
  /**
734
753
  * Create a short-lived voice transcription token for issue reporting.
735
754
  */
@@ -963,7 +982,7 @@ declare class SPAPSClient<SecureMessageMetadata extends Record<string, any> = Re
963
982
  updateProductSuperAdmin: (productId: string, updates: any) => Promise<any>;
964
983
  deleteProductSuperAdmin: (productId: string) => Promise<any>;
965
984
  createProductWithPrice: (payload: any) => Promise<any>;
966
- createProductWithPriceSuperAdmin: (productId: string, payload: any) => Promise<any>;
985
+ createProductWithPriceSuperAdmin: (applicationId: string, payload: any) => Promise<any>;
967
986
  setDefaultPrice: (productId: string, payload: {
968
987
  price_id: string;
969
988
  }) => Promise<any>;
@@ -1091,17 +1110,50 @@ declare class SPAPSClient<SecureMessageMetadata extends Record<string, any> = Re
1091
1110
  */
1092
1111
  getCheckoutStatus: (sessionId: string) => Promise<DayrateCheckoutStatusResponse>;
1093
1112
  };
1113
+ /**
1114
+ * Server-side usage control.
1115
+ *
1116
+ * These endpoints require a secret SPAPS key. Browser clients should call
1117
+ * their own backend, then the backend should authorize before work and record
1118
+ * after work completes.
1119
+ */
1120
+ usage: {
1121
+ /**
1122
+ * List active usage feature definitions for this application.
1123
+ */
1124
+ listFeatures: () => Promise<UsageControlFeaturesResponse>;
1125
+ /**
1126
+ * Fetch current window usage status without creating an authorization.
1127
+ */
1128
+ getStatus: (query: UsageControlStatusRequest) => Promise<UsageControlStatusResponse>;
1129
+ /**
1130
+ * Authorize proposed usage before a downstream job, inference, or action runs.
1131
+ */
1132
+ authorize: (payload: UsageControlAuthorizeRequest) => Promise<UsageControlAuthorizeResponse>;
1133
+ /**
1134
+ * Record immutable usage after work completes.
1135
+ */
1136
+ record: (payload: UsageControlRecordRequest) => Promise<UsageControlRecordResponse>;
1137
+ /**
1138
+ * List usage events in deterministic newest-first order.
1139
+ */
1140
+ listHistory: (query?: UsageControlHistoryRequest) => Promise<UsageControlHistoryResponse>;
1141
+ };
1094
1142
  createCheckoutSession(priceId: string, successUrl: string, cancelUrl?: string): Promise<{
1095
1143
  data: CheckoutSession;
1096
1144
  }>;
1097
- getSubscription(): Promise<{
1098
- data: Subscription;
1145
+ getSubscription(subscriptionId?: string): Promise<{
1146
+ data: Subscription | Subscription[];
1099
1147
  }>;
1100
- cancelSubscription(): Promise<void>;
1101
- getUsageBalance(): Promise<{
1102
- data: UsageBalance;
1148
+ cancelSubscription(subscriptionId?: string, options?: {
1149
+ immediately?: boolean;
1150
+ }): Promise<{
1151
+ data: Subscription;
1103
1152
  }>;
1104
- recordUsage(feature: string, amount: number): Promise<void>;
1153
+ authorizeUsage(payload: UsageControlAuthorizeRequest): Promise<UsageControlAuthorizeResponse>;
1154
+ recordUsage(payload: UsageControlRecordRequest): Promise<UsageControlRecordResponse>;
1155
+ getUsageStatus(query: UsageControlStatusRequest): Promise<UsageControlStatusResponse>;
1156
+ listUsageHistory(query?: UsageControlHistoryRequest): Promise<UsageControlHistoryResponse>;
1105
1157
  private createSecureMessage;
1106
1158
  private listSecureMessages;
1107
1159
  /**
@@ -1283,4 +1335,4 @@ declare function createServerClient(secretKey: string, options?: Omit<SPAPSConfi
1283
1335
  */
1284
1336
  declare function detectKeyType(key: string): ApiKeyType | null;
1285
1337
 
1286
- export { type AdminConfig, type ApiKeyType, type CheckoutLineItem, type CheckoutLineItemPriceData, type CreateCheckoutSessionPayload, type CreateSkillEvalCaseRequest, type CreateSkillEvalGovernanceSnapshotRequest, DEFAULT_ADMIN_ACCOUNTS, type EmailSendOptions, type EmailSendResult, type EmailTemplate, type EmailTemplatePreview, type EntitlementCheckResult, type EntitlementListParams, type FeatureContext, type FeatureDefinition, FeatureEvaluator, type HeaderProvider, type ImportSkillEvalGovernanceOutcomeRequest, type IssueReportListParams, type IssueReportStatusParams, type PermissionCheckResult, PermissionChecker, type RespondToSkillEvalReviewRequest, type RevealSkillEvalEvidenceRequest, RoleHierarchy, SPAPSClient as SPAPS, SPAPSClient, type SPAPSConfig, type SPAPSEnvelope, type SkillEvalAccessMode, type SkillEvalActorAccess, type SkillEvalCandidateInput, type SkillEvalCandidateResponse, type SkillEvalCasePolicy, type SkillEvalCaseResponse, type SkillEvalConfidence, type SkillEvalCreateOptions, type SkillEvalDisclosurePolicy, type SkillEvalEligibilitySource, type SkillEvalGovernanceOutcomeResult, type SkillEvalGovernancePurpose, type SkillEvalGovernanceSnapshotResult, type SkillEvalInsight, type SkillEvalInsightsResponse, type SkillEvalModelEffort, type SkillEvalMutationOptions, type SkillEvalPosterResponse, type SkillEvalPosterResponseResult, type SkillEvalRevealField, type SkillEvalRevealResult, type SkillEvalReviewMarkInput, type SkillEvalReviewMarkKind, type SkillEvalReviewResponse, type SkillEvalReviewRoom, type SkillEvalReviewerEligibilityInput, type SkillEvalRewardEvent, type SubmitSkillEvalReviewRequest, type TemplateVariable, TokenManager, WalletUtils, WebSocketAuthHelper, type WebSocketAuthHelperConfig, type X402ExecuteActionOptions, X402PaymentRequiredSDKError, type X402ReceiptListParams, type X402VerifyHandoffOptions, canAccessAdmin, createBrowserClient, createPermissionChecker, createServerClient, SPAPSClient as default, defaultPermissionChecker, detectKeyType, getRoleAwareErrorMessage, getUserDisplay, getUserRole, hasPermission, isAdminAccount, isEnvelope, isErrorEnvelope, isSuccessEnvelope, unwrapEnvelope, unwrapNestedData, verifyCryptoWebhookSignature };
1338
+ export { type AdminConfig, type ApiKeyType, type CheckoutLineItem, type CheckoutLineItemPriceData, type CreateCheckoutSessionPayload, type CreateSkillEvalCaseRequest, type CreateSkillEvalGovernanceSnapshotRequest, DEFAULT_ADMIN_ACCOUNTS, type EmailSendOptions, type EmailSendResult, type EmailTemplate, type EmailTemplatePreview, type EntitlementCheckResult, type EntitlementListParams, type FeatureContext, type FeatureDefinition, FeatureEvaluator, type HeaderProvider, type ImportSkillEvalGovernanceOutcomeRequest, type IssueReportAttachmentUploadOptions, type IssueReportListParams, type IssueReportStatusParams, type PermissionCheckResult, PermissionChecker, type RespondToSkillEvalReviewRequest, type RevealSkillEvalEvidenceRequest, RoleHierarchy, SPAPSClient as SPAPS, SPAPSClient, type SPAPSConfig, type SPAPSEnvelope, type SkillEvalAccessMode, type SkillEvalActorAccess, type SkillEvalCandidateInput, type SkillEvalCandidateResponse, type SkillEvalCasePolicy, type SkillEvalCaseResponse, type SkillEvalConfidence, type SkillEvalCreateOptions, type SkillEvalDisclosurePolicy, type SkillEvalEligibilitySource, type SkillEvalGovernanceOutcomeResult, type SkillEvalGovernancePurpose, type SkillEvalGovernanceSnapshotResult, type SkillEvalInsight, type SkillEvalInsightsResponse, type SkillEvalModelEffort, type SkillEvalMutationOptions, type SkillEvalPosterResponse, type SkillEvalPosterResponseResult, type SkillEvalRevealField, type SkillEvalRevealResult, type SkillEvalReviewMarkInput, type SkillEvalReviewMarkKind, type SkillEvalReviewResponse, type SkillEvalReviewRoom, type SkillEvalReviewerEligibilityInput, type SkillEvalRewardEvent, type SubmitSkillEvalReviewRequest, type TemplateVariable, TokenManager, WalletUtils, WebSocketAuthHelper, type WebSocketAuthHelperConfig, type X402ExecuteActionOptions, X402PaymentRequiredSDKError, type X402ReceiptListParams, type X402VerifyHandoffOptions, canAccessAdmin, createBrowserClient, createPermissionChecker, createServerClient, SPAPSClient as default, defaultPermissionChecker, detectKeyType, getRoleAwareErrorMessage, getUserDisplay, getUserRole, hasPermission, isAdminAccount, isEnvelope, isErrorEnvelope, isSuccessEnvelope, unwrapEnvelope, unwrapNestedData, verifyCryptoWebhookSignature };
package/dist/index.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import * as spaps_types from 'spaps-types';
2
- import { ResourceType, Entitlement, CreateProductRequest, Product, UpdateProductRequest, CreatePriceRequest, Price, ProductSyncResult, CryptoReconcileRequest, CreateSecureMessageRequest, SecureMessage, IssueReportScope, IssueReportStatusResult, IssueReportStatus, IssueReportListResult, IssueReport, CreateIssueReportRequest, IssueReportingVoiceTokenResult, UpdateIssueReportRequest, ReplyIssueReportRequest, CreateAppLinkRequest, AppLink, AuthResponse, User as User$1, CreateCryptoInvoiceRequest, CryptoInvoiceStatusSnapshot, CheckoutSession, X402ResourceStatusResponse, X402ActionResponse, X402ReceiptResponse, X402ReceiptListResponse, X402HandoffVerification, DayrateAvailabilityResponse, DayrateBookingRequest, DayrateBookingResponse, DayrateMultiBookingRequest, DayrateMultiBookingResponse, DayrateX402BookingRequest, DayrateX402BookingResponse, DayrateCheckoutStatusResponse, Subscription, UsageBalance, VerifyCryptoWebhookSignatureOptions } from 'spaps-types';
3
- export { AdminPermission, AdminRole, AdminUser, ApiResponse, AppLink, AuthResponse, CheckoutSession, CreateAppLinkRequest, CreateCryptoInvoiceRequest, CreateIssueReportRequest, CreatePriceRequest, CreateProductRequest, CreateSecureMessageInput, CreateSecureMessageRequest, CryptoInvoice, CryptoInvoiceResponse, CryptoInvoiceStatusSnapshot, CryptoReconcileRequest, DayrateAvailabilityResponse, DayrateAvailableSlot, DayrateBookingRequest, DayrateBookingResponse, DayrateCheckoutStatus, DayrateCheckoutStatusBooking, DayrateCheckoutStatusResponse, DayrateDayOfWeek, DayrateMultiBookingRequest, DayrateMultiBookingResponse, DayratePriceBreakdown, DayrateSlotType, DayrateX402BookingRequest, DayrateX402BookingResponse, Entitlement, IssueReport, IssueReportListResult, IssueReportStatus, IssueReportStatusResult, IssueReportTarget, IssueReportingInputMode, IssueReportingVoiceProvider, IssueReportingVoiceTokenResult, LinkedIssueReportCase, Price, Product, ProductSyncResult, ReplyIssueReportRequest, ResourceType, SecureMessage, SecureMessageOutput, Subscription, TokenPair, UpdateIssueReportRequest, UpdateProductRequest, UsageBalance, User, UserProfile, UserRole, UserWallet, VerifyCryptoWebhookSignatureOptions, X402ActionFreeResponse, X402ActionOutcome, X402ActionPendingResponse, X402ActionReplayedResponse, X402ActionResponse, X402ActionSettledResponse, X402ExecuteActionRequest, X402HandoffAuthorization, X402HandoffVerification, X402HandoffVerifyRequest, X402PaymentAccept, X402PaymentRequirement, X402ProjectionStatus, X402Receipt, X402ReceiptListResponse, X402ReceiptResponse, X402ReceiptStatus, X402Resource, X402ResourceStatus, X402ResourceStatusResponse, atomicToMoneyUnits, createSecureMessageRequestSchema, isX402PaymentRequired, isX402ResourceStatus, moneyUnitsToAtomic, roundHalfToPositiveInfinity, secureMessageMetadataSchema, secureMessageSchema, validatePositiveAmountAtomic } from 'spaps-types';
2
+ import { ResourceType, Entitlement, CreateProductRequest, Product, UpdateProductRequest, CreatePriceRequest, Price, ProductSyncResult, CryptoReconcileRequest, CreateSecureMessageRequest, SecureMessage, IssueReportScope, IssueReportStatusResult, IssueReportStatus, IssueReportListResult, IssueReport, CreateIssueReportRequest, IssueReportAttachmentOut, ListIssueReportAttachmentsResponse, IssueReportAttachmentAccessResponse, IssueReportingVoiceTokenResult, UpdateIssueReportRequest, ReplyIssueReportRequest, CreateAppLinkRequest, AppLink, AuthResponse, User as User$1, CreateCryptoInvoiceRequest, CryptoInvoiceStatusSnapshot, CheckoutSession, X402ResourceStatusResponse, X402ActionResponse, X402ReceiptResponse, X402ReceiptListResponse, X402HandoffVerification, DayrateAvailabilityResponse, DayrateBookingRequest, DayrateBookingResponse, DayrateMultiBookingRequest, DayrateMultiBookingResponse, DayrateX402BookingRequest, DayrateX402BookingResponse, DayrateCheckoutStatusResponse, UsageControlFeaturesResponse, UsageControlStatusRequest, UsageControlStatusResponse, UsageControlAuthorizeRequest, UsageControlAuthorizeResponse, UsageControlRecordRequest, UsageControlRecordResponse, UsageControlHistoryRequest, UsageControlHistoryResponse, Subscription, VerifyCryptoWebhookSignatureOptions } from 'spaps-types';
3
+ export { AdminPermission, AdminRole, AdminUser, ApiResponse, AppLink, AuthResponse, CheckoutSession, CreateAppLinkRequest, CreateCryptoInvoiceRequest, CreateIssueReportRequest, CreatePriceRequest, CreateProductRequest, CreateSecureMessageInput, CreateSecureMessageRequest, CryptoInvoice, CryptoInvoiceResponse, CryptoInvoiceStatusSnapshot, CryptoReconcileRequest, DayrateAvailabilityResponse, DayrateAvailableSlot, DayrateBookingRequest, DayrateBookingResponse, DayrateCheckoutStatus, DayrateCheckoutStatusBooking, DayrateCheckoutStatusResponse, DayrateDayOfWeek, DayrateMultiBookingRequest, DayrateMultiBookingResponse, DayratePriceBreakdown, DayrateSlotType, DayrateX402BookingRequest, DayrateX402BookingResponse, Entitlement, IssueReport, IssueReportAttachmentAccessResponse, IssueReportAttachmentOut, IssueReportListResult, IssueReportStatus, IssueReportStatusResult, IssueReportTarget, IssueReportingInputMode, IssueReportingVoiceProvider, IssueReportingVoiceTokenResult, LinkedIssueReportCase, ListIssueReportAttachmentsResponse, Price, Product, ProductSyncResult, ReplyIssueReportRequest, ResourceType, SecureMessage, SecureMessageOutput, Subscription, TokenPair, UpdateIssueReportRequest, UpdateProductRequest, UsageControlAuthorizeRequest, UsageControlAuthorizeResponse, UsageControlDecision, UsageControlDimensions, UsageControlError, UsageControlErrorCode, UsageControlFeaturesResponse, UsageControlHistoryRequest, UsageControlHistoryResponse, UsageControlLedgerEvent, UsageControlRecordRequest, UsageControlRecordResponse, UsageControlRecordStatus, UsageControlStatusRequest, UsageControlStatusResponse, User, UserProfile, UserRole, UserWallet, VerifyCryptoWebhookSignatureOptions, X402ActionFreeResponse, X402ActionOutcome, X402ActionPendingResponse, X402ActionReplayedResponse, X402ActionResponse, X402ActionSettledResponse, X402ExecuteActionRequest, X402HandoffAuthorization, X402HandoffVerification, X402HandoffVerifyRequest, X402PaymentAccept, X402PaymentRequirement, X402ProjectionStatus, X402Receipt, X402ReceiptListResponse, X402ReceiptResponse, X402ReceiptStatus, X402Resource, X402ResourceStatus, X402ResourceStatusResponse, atomicToMoneyUnits, createSecureMessageRequestSchema, isX402PaymentRequired, isX402ResourceStatus, moneyUnitsToAtomic, roundHalfToPositiveInfinity, secureMessageMetadataSchema, secureMessageSchema, validatePositiveAmountAtomic } from 'spaps-types';
4
4
 
5
5
  /**
6
6
  * Permission checking utilities for SPAPS SDK
@@ -360,6 +360,9 @@ interface IssueReportListParams {
360
360
  interface IssueReportStatusParams {
361
361
  scope?: SupportedIssueReportScope;
362
362
  }
363
+ interface IssueReportAttachmentUploadOptions {
364
+ filename?: string;
365
+ }
363
366
  declare class X402PaymentRequiredSDKError extends Error {
364
367
  readonly paymentRequiredHeader: string;
365
368
  readonly response: unknown;
@@ -730,6 +733,22 @@ declare class SPAPSClient<SecureMessageMetadata extends Record<string, any> = Re
730
733
  * Create a new canonical issue report.
731
734
  */
732
735
  create: (payload: CreateIssueReportRequest) => Promise<IssueReport>;
736
+ /**
737
+ * Upload one private pending screenshot attachment.
738
+ */
739
+ uploadAttachment: (file: Blob, options?: IssueReportAttachmentUploadOptions) => Promise<IssueReportAttachmentOut>;
740
+ /**
741
+ * List non-deleted screenshot attachments for one issue report.
742
+ */
743
+ listAttachments: (issueReportId: string) => Promise<ListIssueReportAttachmentsResponse>;
744
+ /**
745
+ * Return a short-lived private attachment access URL.
746
+ */
747
+ getAttachmentAccessUrl: (attachmentId: string) => Promise<IssueReportAttachmentAccessResponse>;
748
+ /**
749
+ * Soft-delete one owned screenshot attachment.
750
+ */
751
+ deleteAttachment: (attachmentId: string) => Promise<void>;
733
752
  /**
734
753
  * Create a short-lived voice transcription token for issue reporting.
735
754
  */
@@ -963,7 +982,7 @@ declare class SPAPSClient<SecureMessageMetadata extends Record<string, any> = Re
963
982
  updateProductSuperAdmin: (productId: string, updates: any) => Promise<any>;
964
983
  deleteProductSuperAdmin: (productId: string) => Promise<any>;
965
984
  createProductWithPrice: (payload: any) => Promise<any>;
966
- createProductWithPriceSuperAdmin: (productId: string, payload: any) => Promise<any>;
985
+ createProductWithPriceSuperAdmin: (applicationId: string, payload: any) => Promise<any>;
967
986
  setDefaultPrice: (productId: string, payload: {
968
987
  price_id: string;
969
988
  }) => Promise<any>;
@@ -1091,17 +1110,50 @@ declare class SPAPSClient<SecureMessageMetadata extends Record<string, any> = Re
1091
1110
  */
1092
1111
  getCheckoutStatus: (sessionId: string) => Promise<DayrateCheckoutStatusResponse>;
1093
1112
  };
1113
+ /**
1114
+ * Server-side usage control.
1115
+ *
1116
+ * These endpoints require a secret SPAPS key. Browser clients should call
1117
+ * their own backend, then the backend should authorize before work and record
1118
+ * after work completes.
1119
+ */
1120
+ usage: {
1121
+ /**
1122
+ * List active usage feature definitions for this application.
1123
+ */
1124
+ listFeatures: () => Promise<UsageControlFeaturesResponse>;
1125
+ /**
1126
+ * Fetch current window usage status without creating an authorization.
1127
+ */
1128
+ getStatus: (query: UsageControlStatusRequest) => Promise<UsageControlStatusResponse>;
1129
+ /**
1130
+ * Authorize proposed usage before a downstream job, inference, or action runs.
1131
+ */
1132
+ authorize: (payload: UsageControlAuthorizeRequest) => Promise<UsageControlAuthorizeResponse>;
1133
+ /**
1134
+ * Record immutable usage after work completes.
1135
+ */
1136
+ record: (payload: UsageControlRecordRequest) => Promise<UsageControlRecordResponse>;
1137
+ /**
1138
+ * List usage events in deterministic newest-first order.
1139
+ */
1140
+ listHistory: (query?: UsageControlHistoryRequest) => Promise<UsageControlHistoryResponse>;
1141
+ };
1094
1142
  createCheckoutSession(priceId: string, successUrl: string, cancelUrl?: string): Promise<{
1095
1143
  data: CheckoutSession;
1096
1144
  }>;
1097
- getSubscription(): Promise<{
1098
- data: Subscription;
1145
+ getSubscription(subscriptionId?: string): Promise<{
1146
+ data: Subscription | Subscription[];
1099
1147
  }>;
1100
- cancelSubscription(): Promise<void>;
1101
- getUsageBalance(): Promise<{
1102
- data: UsageBalance;
1148
+ cancelSubscription(subscriptionId?: string, options?: {
1149
+ immediately?: boolean;
1150
+ }): Promise<{
1151
+ data: Subscription;
1103
1152
  }>;
1104
- recordUsage(feature: string, amount: number): Promise<void>;
1153
+ authorizeUsage(payload: UsageControlAuthorizeRequest): Promise<UsageControlAuthorizeResponse>;
1154
+ recordUsage(payload: UsageControlRecordRequest): Promise<UsageControlRecordResponse>;
1155
+ getUsageStatus(query: UsageControlStatusRequest): Promise<UsageControlStatusResponse>;
1156
+ listUsageHistory(query?: UsageControlHistoryRequest): Promise<UsageControlHistoryResponse>;
1105
1157
  private createSecureMessage;
1106
1158
  private listSecureMessages;
1107
1159
  /**
@@ -1283,4 +1335,4 @@ declare function createServerClient(secretKey: string, options?: Omit<SPAPSConfi
1283
1335
  */
1284
1336
  declare function detectKeyType(key: string): ApiKeyType | null;
1285
1337
 
1286
- export { type AdminConfig, type ApiKeyType, type CheckoutLineItem, type CheckoutLineItemPriceData, type CreateCheckoutSessionPayload, type CreateSkillEvalCaseRequest, type CreateSkillEvalGovernanceSnapshotRequest, DEFAULT_ADMIN_ACCOUNTS, type EmailSendOptions, type EmailSendResult, type EmailTemplate, type EmailTemplatePreview, type EntitlementCheckResult, type EntitlementListParams, type FeatureContext, type FeatureDefinition, FeatureEvaluator, type HeaderProvider, type ImportSkillEvalGovernanceOutcomeRequest, type IssueReportListParams, type IssueReportStatusParams, type PermissionCheckResult, PermissionChecker, type RespondToSkillEvalReviewRequest, type RevealSkillEvalEvidenceRequest, RoleHierarchy, SPAPSClient as SPAPS, SPAPSClient, type SPAPSConfig, type SPAPSEnvelope, type SkillEvalAccessMode, type SkillEvalActorAccess, type SkillEvalCandidateInput, type SkillEvalCandidateResponse, type SkillEvalCasePolicy, type SkillEvalCaseResponse, type SkillEvalConfidence, type SkillEvalCreateOptions, type SkillEvalDisclosurePolicy, type SkillEvalEligibilitySource, type SkillEvalGovernanceOutcomeResult, type SkillEvalGovernancePurpose, type SkillEvalGovernanceSnapshotResult, type SkillEvalInsight, type SkillEvalInsightsResponse, type SkillEvalModelEffort, type SkillEvalMutationOptions, type SkillEvalPosterResponse, type SkillEvalPosterResponseResult, type SkillEvalRevealField, type SkillEvalRevealResult, type SkillEvalReviewMarkInput, type SkillEvalReviewMarkKind, type SkillEvalReviewResponse, type SkillEvalReviewRoom, type SkillEvalReviewerEligibilityInput, type SkillEvalRewardEvent, type SubmitSkillEvalReviewRequest, type TemplateVariable, TokenManager, WalletUtils, WebSocketAuthHelper, type WebSocketAuthHelperConfig, type X402ExecuteActionOptions, X402PaymentRequiredSDKError, type X402ReceiptListParams, type X402VerifyHandoffOptions, canAccessAdmin, createBrowserClient, createPermissionChecker, createServerClient, SPAPSClient as default, defaultPermissionChecker, detectKeyType, getRoleAwareErrorMessage, getUserDisplay, getUserRole, hasPermission, isAdminAccount, isEnvelope, isErrorEnvelope, isSuccessEnvelope, unwrapEnvelope, unwrapNestedData, verifyCryptoWebhookSignature };
1338
+ export { type AdminConfig, type ApiKeyType, type CheckoutLineItem, type CheckoutLineItemPriceData, type CreateCheckoutSessionPayload, type CreateSkillEvalCaseRequest, type CreateSkillEvalGovernanceSnapshotRequest, DEFAULT_ADMIN_ACCOUNTS, type EmailSendOptions, type EmailSendResult, type EmailTemplate, type EmailTemplatePreview, type EntitlementCheckResult, type EntitlementListParams, type FeatureContext, type FeatureDefinition, FeatureEvaluator, type HeaderProvider, type ImportSkillEvalGovernanceOutcomeRequest, type IssueReportAttachmentUploadOptions, type IssueReportListParams, type IssueReportStatusParams, type PermissionCheckResult, PermissionChecker, type RespondToSkillEvalReviewRequest, type RevealSkillEvalEvidenceRequest, RoleHierarchy, SPAPSClient as SPAPS, SPAPSClient, type SPAPSConfig, type SPAPSEnvelope, type SkillEvalAccessMode, type SkillEvalActorAccess, type SkillEvalCandidateInput, type SkillEvalCandidateResponse, type SkillEvalCasePolicy, type SkillEvalCaseResponse, type SkillEvalConfidence, type SkillEvalCreateOptions, type SkillEvalDisclosurePolicy, type SkillEvalEligibilitySource, type SkillEvalGovernanceOutcomeResult, type SkillEvalGovernancePurpose, type SkillEvalGovernanceSnapshotResult, type SkillEvalInsight, type SkillEvalInsightsResponse, type SkillEvalModelEffort, type SkillEvalMutationOptions, type SkillEvalPosterResponse, type SkillEvalPosterResponseResult, type SkillEvalRevealField, type SkillEvalRevealResult, type SkillEvalReviewMarkInput, type SkillEvalReviewMarkKind, type SkillEvalReviewResponse, type SkillEvalReviewRoom, type SkillEvalReviewerEligibilityInput, type SkillEvalRewardEvent, type SubmitSkillEvalReviewRequest, type TemplateVariable, TokenManager, WalletUtils, WebSocketAuthHelper, type WebSocketAuthHelperConfig, type X402ExecuteActionOptions, X402PaymentRequiredSDKError, type X402ReceiptListParams, type X402VerifyHandoffOptions, canAccessAdmin, createBrowserClient, createPermissionChecker, createServerClient, SPAPSClient as default, defaultPermissionChecker, detectKeyType, getRoleAwareErrorMessage, getUserDisplay, getUserRole, hasPermission, isAdminAccount, isEnvelope, isErrorEnvelope, isSuccessEnvelope, unwrapEnvelope, unwrapNestedData, verifyCryptoWebhookSignature };
package/dist/index.js CHANGED
@@ -757,6 +757,61 @@ var SPAPSClient = class _SPAPSClient {
757
757
  );
758
758
  return this.unwrapApiResponse(res, "Failed to create issue report");
759
759
  },
760
+ /**
761
+ * Upload one private pending screenshot attachment.
762
+ */
763
+ uploadAttachment: async (file, options) => {
764
+ if (typeof FormData === "undefined") {
765
+ throw new Error("FormData is required to upload issue report attachments");
766
+ }
767
+ const formData = new FormData();
768
+ const fileWithOptionalName = file;
769
+ const inferredFilename = typeof fileWithOptionalName.name === "string" ? fileWithOptionalName.name : void 0;
770
+ const filename = options?.filename ?? inferredFilename;
771
+ if (filename) {
772
+ formData.append("file", file, filename);
773
+ } else {
774
+ formData.append("file", file);
775
+ }
776
+ const res = await this.client.post(
777
+ "/api/v1/issue-reports/attachments",
778
+ formData,
779
+ this.accessToken ? { headers: { Authorization: `Bearer ${this.accessToken}` } } : void 0
780
+ );
781
+ return this.unwrapApiResponse(res, "Failed to upload issue report attachment");
782
+ },
783
+ /**
784
+ * List non-deleted screenshot attachments for one issue report.
785
+ */
786
+ listAttachments: async (issueReportId) => {
787
+ const res = await this.client.get(
788
+ `/api/v1/issue-reports/${encodeURIComponent(issueReportId)}/attachments`,
789
+ this.accessToken ? { headers: { Authorization: `Bearer ${this.accessToken}` } } : void 0
790
+ );
791
+ return this.unwrapApiResponse(res, "Failed to list issue report attachments");
792
+ },
793
+ /**
794
+ * Return a short-lived private attachment access URL.
795
+ */
796
+ getAttachmentAccessUrl: async (attachmentId) => {
797
+ const res = await this.client.get(
798
+ `/api/v1/issue-reports/attachments/${encodeURIComponent(attachmentId)}/access`,
799
+ this.accessToken ? { headers: { Authorization: `Bearer ${this.accessToken}` } } : void 0
800
+ );
801
+ return this.unwrapApiResponse(
802
+ res,
803
+ "Failed to get issue report attachment access URL"
804
+ );
805
+ },
806
+ /**
807
+ * Soft-delete one owned screenshot attachment.
808
+ */
809
+ deleteAttachment: async (attachmentId) => {
810
+ await this.client.delete(
811
+ `/api/v1/issue-reports/attachments/${encodeURIComponent(attachmentId)}`,
812
+ this.accessToken ? { headers: { Authorization: `Bearer ${this.accessToken}` } } : void 0
813
+ );
814
+ },
760
815
  /**
761
816
  * Create a short-lived voice transcription token for issue reporting.
762
817
  */
@@ -1295,9 +1350,9 @@ var SPAPSClient = class _SPAPSClient {
1295
1350
  const res = await this.client.post("/api/stripe/products/with-price", payload, { headers: { Authorization: `Bearer ${this.accessToken}` } });
1296
1351
  return this.unwrapApiResponse(res, "Failed to create product with price");
1297
1352
  },
1298
- createProductWithPriceSuperAdmin: async (productId, payload) => {
1353
+ createProductWithPriceSuperAdmin: async (applicationId, payload) => {
1299
1354
  if (!this.accessToken) throw new Error("Authentication required. Please authenticate first.");
1300
- const res = await this.client.post(`/api/stripe/products/super-admin/${productId}/with-price`, payload, { headers: { Authorization: `Bearer ${this.accessToken}` } });
1355
+ const res = await this.client.post(`/api/stripe/products/super-admin/${applicationId}/with-price`, payload, { headers: { Authorization: `Bearer ${this.accessToken}` } });
1301
1356
  return this.unwrapApiResponse(res, "Failed to create product with price (super admin)");
1302
1357
  },
1303
1358
  setDefaultPrice: async (productId, payload) => {
@@ -1617,29 +1672,96 @@ var SPAPSClient = class _SPAPSClient {
1617
1672
  return this.unwrapApiResponse(res, "Failed to get checkout status");
1618
1673
  }
1619
1674
  };
1675
+ /**
1676
+ * Server-side usage control.
1677
+ *
1678
+ * These endpoints require a secret SPAPS key. Browser clients should call
1679
+ * their own backend, then the backend should authorize before work and record
1680
+ * after work completes.
1681
+ */
1682
+ usage = {
1683
+ /**
1684
+ * List active usage feature definitions for this application.
1685
+ */
1686
+ listFeatures: async () => {
1687
+ const res = await this.client.get("/api/usage/features");
1688
+ return this.unwrapApiResponse(res, "Failed to list usage features");
1689
+ },
1690
+ /**
1691
+ * Fetch current window usage status without creating an authorization.
1692
+ */
1693
+ getStatus: async (query) => {
1694
+ const q = new URLSearchParams();
1695
+ q.set("feature_key", query.feature_key);
1696
+ q.set("resource_type", query.resource_type);
1697
+ if (query.resource_id) q.set("resource_id", query.resource_id);
1698
+ if (query.subject_user_id) q.set("subject_user_id", query.subject_user_id);
1699
+ const res = await this.client.get(`/api/usage/status?${q.toString()}`);
1700
+ return this.unwrapApiResponse(res, "Failed to get usage status");
1701
+ },
1702
+ /**
1703
+ * Authorize proposed usage before a downstream job, inference, or action runs.
1704
+ */
1705
+ authorize: async (payload) => {
1706
+ const res = await this.client.post("/api/usage/authorize", payload);
1707
+ return this.unwrapApiResponse(res, "Failed to authorize usage");
1708
+ },
1709
+ /**
1710
+ * Record immutable usage after work completes.
1711
+ */
1712
+ record: async (payload) => {
1713
+ const res = await this.client.post("/api/usage/record", payload);
1714
+ return this.unwrapApiResponse(res, "Failed to record usage");
1715
+ },
1716
+ /**
1717
+ * List usage events in deterministic newest-first order.
1718
+ */
1719
+ listHistory: async (query = {}) => {
1720
+ const q = new URLSearchParams();
1721
+ if (query.feature_key) q.set("feature_key", query.feature_key);
1722
+ if (query.resource_type) q.set("resource_type", query.resource_type);
1723
+ if (query.resource_id) q.set("resource_id", query.resource_id);
1724
+ if (query.subject_user_id) q.set("subject_user_id", query.subject_user_id);
1725
+ if (query.limit) q.set("limit", String(query.limit));
1726
+ if (typeof query.offset === "number") q.set("offset", String(query.offset));
1727
+ const suffix = q.toString() ? `?${q.toString()}` : "";
1728
+ const res = await this.client.get(`/api/usage/history${suffix}`);
1729
+ return this.unwrapApiResponse(res, "Failed to list usage history");
1730
+ }
1731
+ };
1620
1732
  // Stripe Methods
1621
1733
  async createCheckoutSession(priceId, successUrl, cancelUrl) {
1622
- return this.client.post("/api/stripe/create-checkout-session", {
1623
- price_id: priceId,
1734
+ return this.client.post("/api/stripe/checkout-sessions", {
1735
+ mode: "payment",
1736
+ line_items: [{ price_id: priceId, quantity: 1 }],
1624
1737
  success_url: successUrl,
1625
- cancel_url: cancelUrl
1738
+ cancel_url: cancelUrl ?? successUrl
1626
1739
  });
1627
1740
  }
1628
- async getSubscription() {
1629
- return this.client.get("/api/stripe/subscription");
1741
+ async getSubscription(subscriptionId) {
1742
+ if (subscriptionId) {
1743
+ return this.client.get(`/api/stripe/subscription/${subscriptionId}`);
1744
+ }
1745
+ return this.client.get("/api/stripe/subscriptions");
1630
1746
  }
1631
- async cancelSubscription() {
1632
- await this.client.delete("/api/stripe/subscription");
1747
+ async cancelSubscription(subscriptionId, options = {}) {
1748
+ if (!subscriptionId) {
1749
+ throw new Error("subscriptionId is required to cancel a subscription.");
1750
+ }
1751
+ return this.client.post(`/api/stripe/subscription/${subscriptionId}/cancel`, options);
1633
1752
  }
1634
1753
  // Usage Methods
1635
- async getUsageBalance() {
1636
- return this.client.get("/api/usage/balance");
1754
+ async authorizeUsage(payload) {
1755
+ return this.usage.authorize(payload);
1637
1756
  }
1638
- async recordUsage(feature, amount) {
1639
- await this.client.post("/api/usage/record", {
1640
- feature,
1641
- amount
1642
- });
1757
+ async recordUsage(payload) {
1758
+ return this.usage.record(payload);
1759
+ }
1760
+ async getUsageStatus(query) {
1761
+ return this.usage.getStatus(query);
1762
+ }
1763
+ async listUsageHistory(query = {}) {
1764
+ return this.usage.listHistory(query);
1643
1765
  }
1644
1766
  // Secure Messaging Methods
1645
1767
  async createSecureMessage(payload) {
package/dist/index.mjs CHANGED
@@ -722,6 +722,61 @@ var SPAPSClient = class _SPAPSClient {
722
722
  );
723
723
  return this.unwrapApiResponse(res, "Failed to create issue report");
724
724
  },
725
+ /**
726
+ * Upload one private pending screenshot attachment.
727
+ */
728
+ uploadAttachment: async (file, options) => {
729
+ if (typeof FormData === "undefined") {
730
+ throw new Error("FormData is required to upload issue report attachments");
731
+ }
732
+ const formData = new FormData();
733
+ const fileWithOptionalName = file;
734
+ const inferredFilename = typeof fileWithOptionalName.name === "string" ? fileWithOptionalName.name : void 0;
735
+ const filename = options?.filename ?? inferredFilename;
736
+ if (filename) {
737
+ formData.append("file", file, filename);
738
+ } else {
739
+ formData.append("file", file);
740
+ }
741
+ const res = await this.client.post(
742
+ "/api/v1/issue-reports/attachments",
743
+ formData,
744
+ this.accessToken ? { headers: { Authorization: `Bearer ${this.accessToken}` } } : void 0
745
+ );
746
+ return this.unwrapApiResponse(res, "Failed to upload issue report attachment");
747
+ },
748
+ /**
749
+ * List non-deleted screenshot attachments for one issue report.
750
+ */
751
+ listAttachments: async (issueReportId) => {
752
+ const res = await this.client.get(
753
+ `/api/v1/issue-reports/${encodeURIComponent(issueReportId)}/attachments`,
754
+ this.accessToken ? { headers: { Authorization: `Bearer ${this.accessToken}` } } : void 0
755
+ );
756
+ return this.unwrapApiResponse(res, "Failed to list issue report attachments");
757
+ },
758
+ /**
759
+ * Return a short-lived private attachment access URL.
760
+ */
761
+ getAttachmentAccessUrl: async (attachmentId) => {
762
+ const res = await this.client.get(
763
+ `/api/v1/issue-reports/attachments/${encodeURIComponent(attachmentId)}/access`,
764
+ this.accessToken ? { headers: { Authorization: `Bearer ${this.accessToken}` } } : void 0
765
+ );
766
+ return this.unwrapApiResponse(
767
+ res,
768
+ "Failed to get issue report attachment access URL"
769
+ );
770
+ },
771
+ /**
772
+ * Soft-delete one owned screenshot attachment.
773
+ */
774
+ deleteAttachment: async (attachmentId) => {
775
+ await this.client.delete(
776
+ `/api/v1/issue-reports/attachments/${encodeURIComponent(attachmentId)}`,
777
+ this.accessToken ? { headers: { Authorization: `Bearer ${this.accessToken}` } } : void 0
778
+ );
779
+ },
725
780
  /**
726
781
  * Create a short-lived voice transcription token for issue reporting.
727
782
  */
@@ -1260,9 +1315,9 @@ var SPAPSClient = class _SPAPSClient {
1260
1315
  const res = await this.client.post("/api/stripe/products/with-price", payload, { headers: { Authorization: `Bearer ${this.accessToken}` } });
1261
1316
  return this.unwrapApiResponse(res, "Failed to create product with price");
1262
1317
  },
1263
- createProductWithPriceSuperAdmin: async (productId, payload) => {
1318
+ createProductWithPriceSuperAdmin: async (applicationId, payload) => {
1264
1319
  if (!this.accessToken) throw new Error("Authentication required. Please authenticate first.");
1265
- const res = await this.client.post(`/api/stripe/products/super-admin/${productId}/with-price`, payload, { headers: { Authorization: `Bearer ${this.accessToken}` } });
1320
+ const res = await this.client.post(`/api/stripe/products/super-admin/${applicationId}/with-price`, payload, { headers: { Authorization: `Bearer ${this.accessToken}` } });
1266
1321
  return this.unwrapApiResponse(res, "Failed to create product with price (super admin)");
1267
1322
  },
1268
1323
  setDefaultPrice: async (productId, payload) => {
@@ -1582,29 +1637,96 @@ var SPAPSClient = class _SPAPSClient {
1582
1637
  return this.unwrapApiResponse(res, "Failed to get checkout status");
1583
1638
  }
1584
1639
  };
1640
+ /**
1641
+ * Server-side usage control.
1642
+ *
1643
+ * These endpoints require a secret SPAPS key. Browser clients should call
1644
+ * their own backend, then the backend should authorize before work and record
1645
+ * after work completes.
1646
+ */
1647
+ usage = {
1648
+ /**
1649
+ * List active usage feature definitions for this application.
1650
+ */
1651
+ listFeatures: async () => {
1652
+ const res = await this.client.get("/api/usage/features");
1653
+ return this.unwrapApiResponse(res, "Failed to list usage features");
1654
+ },
1655
+ /**
1656
+ * Fetch current window usage status without creating an authorization.
1657
+ */
1658
+ getStatus: async (query) => {
1659
+ const q = new URLSearchParams();
1660
+ q.set("feature_key", query.feature_key);
1661
+ q.set("resource_type", query.resource_type);
1662
+ if (query.resource_id) q.set("resource_id", query.resource_id);
1663
+ if (query.subject_user_id) q.set("subject_user_id", query.subject_user_id);
1664
+ const res = await this.client.get(`/api/usage/status?${q.toString()}`);
1665
+ return this.unwrapApiResponse(res, "Failed to get usage status");
1666
+ },
1667
+ /**
1668
+ * Authorize proposed usage before a downstream job, inference, or action runs.
1669
+ */
1670
+ authorize: async (payload) => {
1671
+ const res = await this.client.post("/api/usage/authorize", payload);
1672
+ return this.unwrapApiResponse(res, "Failed to authorize usage");
1673
+ },
1674
+ /**
1675
+ * Record immutable usage after work completes.
1676
+ */
1677
+ record: async (payload) => {
1678
+ const res = await this.client.post("/api/usage/record", payload);
1679
+ return this.unwrapApiResponse(res, "Failed to record usage");
1680
+ },
1681
+ /**
1682
+ * List usage events in deterministic newest-first order.
1683
+ */
1684
+ listHistory: async (query = {}) => {
1685
+ const q = new URLSearchParams();
1686
+ if (query.feature_key) q.set("feature_key", query.feature_key);
1687
+ if (query.resource_type) q.set("resource_type", query.resource_type);
1688
+ if (query.resource_id) q.set("resource_id", query.resource_id);
1689
+ if (query.subject_user_id) q.set("subject_user_id", query.subject_user_id);
1690
+ if (query.limit) q.set("limit", String(query.limit));
1691
+ if (typeof query.offset === "number") q.set("offset", String(query.offset));
1692
+ const suffix = q.toString() ? `?${q.toString()}` : "";
1693
+ const res = await this.client.get(`/api/usage/history${suffix}`);
1694
+ return this.unwrapApiResponse(res, "Failed to list usage history");
1695
+ }
1696
+ };
1585
1697
  // Stripe Methods
1586
1698
  async createCheckoutSession(priceId, successUrl, cancelUrl) {
1587
- return this.client.post("/api/stripe/create-checkout-session", {
1588
- price_id: priceId,
1699
+ return this.client.post("/api/stripe/checkout-sessions", {
1700
+ mode: "payment",
1701
+ line_items: [{ price_id: priceId, quantity: 1 }],
1589
1702
  success_url: successUrl,
1590
- cancel_url: cancelUrl
1703
+ cancel_url: cancelUrl ?? successUrl
1591
1704
  });
1592
1705
  }
1593
- async getSubscription() {
1594
- return this.client.get("/api/stripe/subscription");
1706
+ async getSubscription(subscriptionId) {
1707
+ if (subscriptionId) {
1708
+ return this.client.get(`/api/stripe/subscription/${subscriptionId}`);
1709
+ }
1710
+ return this.client.get("/api/stripe/subscriptions");
1595
1711
  }
1596
- async cancelSubscription() {
1597
- await this.client.delete("/api/stripe/subscription");
1712
+ async cancelSubscription(subscriptionId, options = {}) {
1713
+ if (!subscriptionId) {
1714
+ throw new Error("subscriptionId is required to cancel a subscription.");
1715
+ }
1716
+ return this.client.post(`/api/stripe/subscription/${subscriptionId}/cancel`, options);
1598
1717
  }
1599
1718
  // Usage Methods
1600
- async getUsageBalance() {
1601
- return this.client.get("/api/usage/balance");
1719
+ async authorizeUsage(payload) {
1720
+ return this.usage.authorize(payload);
1602
1721
  }
1603
- async recordUsage(feature, amount) {
1604
- await this.client.post("/api/usage/record", {
1605
- feature,
1606
- amount
1607
- });
1722
+ async recordUsage(payload) {
1723
+ return this.usage.record(payload);
1724
+ }
1725
+ async getUsageStatus(query) {
1726
+ return this.usage.getStatus(query);
1727
+ }
1728
+ async listUsageHistory(query = {}) {
1729
+ return this.usage.listHistory(query);
1608
1730
  }
1609
1731
  // Secure Messaging Methods
1610
1732
  async createSecureMessage(payload) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "spaps-sdk",
3
- "version": "1.9.0",
3
+ "version": "1.10.1",
4
4
  "description": "Sweet Potato Authentication & Payment Service SDK - Zero-config client with built-in permission checking, role-based access control, and dayrate scheduling",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -51,7 +51,7 @@
51
51
  "dependencies": {
52
52
  "axios": "^1.15.1",
53
53
  "cross-fetch": "^4.0.0",
54
- "spaps-types": "^1.2.0"
54
+ "spaps-types": "^1.4.0"
55
55
  },
56
56
  "devDependencies": {
57
57
  "@types/node": "^20.10.0",