@spicyapi/sdk 0.2.1 → 0.4.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
@@ -16,9 +16,31 @@ const models = await client.listModels({ includeSchema: true });
16
16
  ```
17
17
 
18
18
  Set `SPICY_API_KEY` with your process secret manager. Task creation and retry may reserve funds; use
19
- stable idempotency keys and explicit user confirmation. Call `quoteTask` with the exact request
20
- first, display its `estimatedCost`, `maxCharge` and `expiresAt`, then call `createTask` with the
21
- unchanged input, `quoteId` and `expectedCost`.
19
+ stable idempotency keys and explicit user confirmation. When a caller needs an exact price
20
+ confirmation, call `quoteTask` with the request, display its `estimatedCost`, `maxCharge` and
21
+ `expiresAt`, then pass unchanged input, `quoteId` and `expectedCost` to `createTask` or `run`.
22
+ Pre-authorized server workflows can submit directly at current pricing; separate health, balance and
23
+ quote requests are not mandatory API steps.
24
+
25
+ ## Submit and wait
26
+
27
+ ```ts
28
+ const model = process.env.SPICY_MODEL;
29
+ const operationId = process.env.SPICY_IDEMPOTENCY_KEY;
30
+ if (!model || !operationId) throw new Error("Set a model and a persisted operation key");
31
+ const task = await client.run(
32
+ { model, input: { prompt: "A quiet mountain lake" } },
33
+ { idempotencyKey: operationId, onAccepted: ({ taskId }) => console.log({ taskId }) },
34
+ );
35
+ if (task.state === "succeeded") {
36
+ console.log(task.output?.assets?.[0]?.url);
37
+ }
38
+ ```
39
+
40
+ Use a model and input supported by its current schema. `run` submits once and handles adaptive
41
+ polling, including pending media transfer. Its local timeout includes submission and waiting; timing
42
+ out or aborting does not cancel the accepted task. Resume with the saved task ID. Use a webhook for
43
+ background jobs to avoid polling entirely.
22
44
 
23
45
  Documentation: <https://spicyapi.ai/en/docs>
24
46
 
@@ -85,7 +107,12 @@ explicit type extension in the official client. Reasoning tokens are already inc
85
107
 
86
108
  Pass a publicly accessible HTTPS image, video, or audio URL directly in the field specified by the
87
109
  model schema. Uploading is optional: local files use `createUploadUrl` → PUT → `commitUploadedFile`,
88
- then the returned account-bound `spicy://` URI (valid for one day).
110
+ then the returned account-bound `spicy://` URI (valid for one day). `uploadFile` wraps these steps;
111
+ `uploadBase64` also accepts a Data URI or raw standard Base64 with an explicit `contentType`. Small
112
+ schema-declared image fields can accept Data URIs directly in `input` (1 MiB decoded per image, 2
113
+ MiB total JSON body, with documented pixel limits). The upload helper is separate and supports
114
+ larger files within upload and model limits. See the
115
+ [media guide](https://docs.spicyapi.ai/en/docs/media).
89
116
 
90
117
  `getTask` and `waitForTask` return ready media in `output.assets`, including `url`, `expiresAt`,
91
118
  MIME, and available dimensions, duration, and byte count. Download `asset.url` directly without
@@ -96,3 +123,25 @@ to refresh a URL; `createDownloadUrl` remains available for older integrations.
96
123
  V2 webhooks use the same result fields. A retry keeps the business event and `request_id`, but
97
124
  refreshes `url` and `expiresAt`. Verify the signature against the exact received body and
98
125
  deduplicate using `request_id`, not a full-body hash. V1 payloads remain unchanged.
126
+
127
+ ## Task history
128
+
129
+ `listTasks` reads one page of metadata for the current API key. It does not fetch each task's
130
+ inputs, results, or media URLs. Keep the UTC dates fixed while paging and pass the returned cursor
131
+ unchanged:
132
+
133
+ ```ts
134
+ const filters = { from: "2026-09-01", to: "2026-09-07", limit: 20 };
135
+ const page = await client.listTasks(filters);
136
+ if (page.hasMore && page.nextCursor) {
137
+ const nextPage = await client.listTasks({ ...filters, cursor: page.nextCursor });
138
+ console.log(nextPage.items);
139
+ }
140
+ ```
141
+
142
+ The UTC interval is `[from,to)`, defaults to seven days ending tomorrow UTC, and cannot exceed 92
143
+ days. Page size defaults to 20 and is capped at 100. Optional `state` and `model` filters narrow the
144
+ results. Each item's `cost` remains an exact decimal USD string; it is final only when `settled` is
145
+ true. Hidden tasks are excluded, so use `getUsage` for settled spending reports. Use `getTask` for a
146
+ selected result, not automatically for every item. History is for discovery and recovery; use `run`,
147
+ `waitForTask`, or webhooks for normal completion tracking.
@@ -353,14 +353,114 @@ paths:
353
353
  '404': { $ref: '#/components/responses/NotFound' }
354
354
  '429': { $ref: '#/components/responses/RateLimited' }
355
355
  '500': { $ref: '#/components/responses/ServerError' }
356
+ /api/v1/jobs:
357
+ get:
358
+ tags: [Tasks]
359
+ operationId: listTasks
360
+ summary: List task history for the authenticated API key
361
+ description: |
362
+ Returns only visible tasks created by this API key and its account,
363
+ newest first by createdAt and taskId. The list contains metadata only;
364
+ use recordInfo for a selected task's result. No input, output, signed
365
+ media URL, or execution-channel information is included.
366
+ Dates form a UTC half-open interval [from,to), up to 92 days.
367
+ Default to is tomorrow in UTC; omitted from is seven days before to.
368
+ Keep all filters, including explicit from/to dates, unchanged while
369
+ paginating with nextCursor. Pages read live state, not a frozen snapshot.
370
+ Unknown, duplicate, or empty query parameters are rejected. Queries
371
+ have a five-second server budget and return 503 on timeout.
372
+ parameters:
373
+ - name: from
374
+ in: query
375
+ schema: { type: string, format: date }
376
+ description: Inclusive UTC date in YYYY-MM-DD format.
377
+ - name: to
378
+ in: query
379
+ schema: { type: string, format: date }
380
+ description: Exclusive UTC date in YYYY-MM-DD format.
381
+ - name: state
382
+ in: query
383
+ schema: { type: string, enum: [queued, running, succeeded, failed, canceled, expired] }
384
+ - name: model
385
+ in: query
386
+ schema: { type: string }
387
+ description: Exact catalog model identifier or a declared alias.
388
+ - name: limit
389
+ in: query
390
+ schema: { type: integer, minimum: 1, maximum: 100, default: 20 }
391
+ - name: cursor
392
+ in: query
393
+ schema: { type: string }
394
+ description: Opaque nextCursor from the previous page; do not construct it yourself.
395
+ responses:
396
+ '200':
397
+ description: Current-key task summaries without prompt or result payloads.
398
+ headers:
399
+ Cache-Control:
400
+ schema: { type: string, const: no-store }
401
+ content:
402
+ application/json:
403
+ schema: { $ref: '#/components/schemas/APIKeyTaskListEnvelope' }
404
+ '400': { $ref: '#/components/responses/BadRequest' }
405
+ '401': { $ref: '#/components/responses/Unauthorized' }
406
+ '403': { $ref: '#/components/responses/Forbidden' }
407
+ '429': { $ref: '#/components/responses/RateLimited' }
408
+ '500': { $ref: '#/components/responses/ServerError' }
409
+ '503': { $ref: '#/components/responses/Unavailable' }
410
+ /api/v1/usage:
411
+ get:
412
+ tags: [Account]
413
+ operationId: getUsage
414
+ summary: Get usage for the authenticated API key
415
+ description: |
416
+ Only tasks created by this API key and its account are included,
417
+ including hidden historical tasks. Other keys and workspaces cannot
418
+ be selected. Unknown or repeated query parameters are rejected.
419
+ Dates form a UTC half-open interval [from,to), up to 92 days.
420
+ The default to is tomorrow in UTC; omitted from is seven days before to.
421
+ Calls are attributed to task creation time. Spend sums only settled
422
+ actual charges for those tasks, never pending holds. Late settlement
423
+ can change a previous day's spend. These are task usage totals, not
424
+ a statement of cash movements or remaining API-key budget.
425
+ An additional account-wide token bucket allows a burst of 30 requests
426
+ and replenishes 30 tokens per minute, shared by all keys on the account.
427
+ Observe Retry-After on 429. This reporting limit fails closed when its
428
+ limiter is unavailable. Queries have a five-second server budget;
429
+ a timeout returns 503 and suggests retrying a shorter date range.
430
+ parameters:
431
+ - name: from
432
+ in: query
433
+ schema: { type: string, format: date }
434
+ description: Inclusive UTC date in YYYY-MM-DD format.
435
+ - name: to
436
+ in: query
437
+ schema: { type: string, format: date }
438
+ description: Exclusive UTC date in YYYY-MM-DD format.
439
+ responses:
440
+ '200':
441
+ description: Current-key usage with exact USD decimal strings and sparse daily/model buckets.
442
+ headers:
443
+ Cache-Control:
444
+ schema: { type: string, const: no-store }
445
+ X-RateLimit-Limit: { $ref: '#/components/headers/RateLimitLimit' }
446
+ X-RateLimit-Remaining: { $ref: '#/components/headers/RateLimitRemaining' }
447
+ content:
448
+ application/json:
449
+ schema: { $ref: '#/components/schemas/APIKeyUsageEnvelope' }
450
+ '400': { $ref: '#/components/responses/BadRequest' }
451
+ '401': { $ref: '#/components/responses/Unauthorized' }
452
+ '403': { $ref: '#/components/responses/Forbidden' }
453
+ '429': { $ref: '#/components/responses/RateLimited' }
454
+ '500': { $ref: '#/components/responses/ServerError' }
455
+ '503': { $ref: '#/components/responses/Unavailable' }
356
456
  /api/v1/chat/credit:
357
457
  get:
358
458
  tags: [Account]
359
459
  operationId: getCreditBalance
360
- summary: Get the account balance
460
+ summary: Get net balance, promotional grants, and approved credit
361
461
  responses:
362
462
  '200':
363
- description: Available, held, and total balance in USD decimal strings.
463
+ description: Net available, held, and total balances plus funding sources in USD decimal strings. Unused credit is separate from wallet funds; model-scoped grants may not pay for every request.
364
464
  content:
365
465
  application/json:
366
466
  schema:
@@ -794,6 +894,47 @@ components:
794
894
  description: Error in the Anthropic `{type:"error",error:{type,message}}` shape.
795
895
  content: { application/json: { schema: { $ref: '#/components/schemas/AnthropicErrorBody' } } }
796
896
  schemas:
897
+ FundingGrantItem:
898
+ type: object
899
+ required: [id, name, amountUsd, availableUsd, heldUsd, spentUsd, status, startsAt, modelSlugs, customerMemo, createdAt]
900
+ properties:
901
+ id: { type: string }
902
+ name: { type: string }
903
+ amountUsd: { type: string }
904
+ availableUsd: { type: string }
905
+ heldUsd: { type: string }
906
+ spentUsd: { type: string }
907
+ status: { type: string }
908
+ startsAt: { type: string, format: date-time }
909
+ expiresAt: { type: [string, 'null'], format: date-time }
910
+ modelSlugs: { type: array, items: { type: string }, description: Empty means all models. }
911
+ customerMemo: { type: string }
912
+ createdAt: { type: string, format: date-time }
913
+ CreditFacilityItem:
914
+ type: object
915
+ required: [enabled, limitUsd, availableUsd, usedUsd, heldUsd, status, version]
916
+ properties:
917
+ enabled: { type: boolean }
918
+ limitUsd: { type: string, description: Approved ceiling, not wallet funds. }
919
+ availableUsd: { type: string }
920
+ usedUsd: { type: string, description: Outstanding settled principal. }
921
+ heldUsd: { type: string, description: Credit reserved for accepted tasks. }
922
+ expiresAt: { type: [string, 'null'], format: date-time }
923
+ status: { type: string }
924
+ version: { type: integer, format: int64 }
925
+ FundingOverview:
926
+ type: object
927
+ required: [balanceUsd, heldUsd, prepaidAvailableUsd, grantAvailableUsd, cashShortfallUsd, credit, grants, grantsHasMore]
928
+ properties:
929
+ balanceUsd: { type: string, description: Net wallet available balance; may be negative for approved credit consumption or external payment recovery. }
930
+ heldUsd: { type: string }
931
+ prepaidAvailableUsd: { type: string, description: Unrestricted prepaid or legacy available funds. }
932
+ grantAvailableUsd: { type: string, description: Active available grants; model applicability is checked at task admission. }
933
+ cashShortfallUsd: { type: string, description: External payment recovery shortfall that cannot be covered by grants or credit. }
934
+ credit: { $ref: '#/components/schemas/CreditFacilityItem' }
935
+ grants: { type: array, items: { $ref: '#/components/schemas/FundingGrantItem' } }
936
+ grantsHasMore: { type: boolean }
937
+
797
938
  EnvelopeBase:
798
939
  type: object
799
940
  required: [code, msg, request_id]
@@ -823,7 +964,16 @@ components:
823
964
  description: Exact `model` value from the model catalog.
824
965
  input:
825
966
  type: object
826
- description: Validated against the selected model's `inputSchema`.
967
+ description: >-
968
+ Validated against the selected model's inputSchema. Declared image fields accept
969
+ public HTTPS URLs, committed spicy:// file URIs, or standard Base64 Data URIs
970
+ (image/jpeg, image/png, image/webp, image/gif). Inline images are limited to
971
+ 1048576 decoded bytes and 8388608 pixels each, 8192 pixels per side, and
972
+ 16 images / 16777216 pixels in total. The complete JSON request must fit within
973
+ 2097152 bytes. Model-specific limits still apply. Bare Base64, SVG and inline
974
+ audio/video are not accepted; use HTTPS or file uploads instead. Inline images
975
+ are stored as account-bound uploaded files; returned input contains file URIs,
976
+ never the original Base64 bytes. Quote validation does not upload or reserve funds.
827
977
  additionalProperties: true
828
978
  callBackUrl:
829
979
  type: string
@@ -863,9 +1013,10 @@ components:
863
1013
  data: { $ref: '#/components/schemas/TaskQuoteResponse' }
864
1014
  CreateTaskResponse:
865
1015
  type: object
866
- required: [taskId, state, estimatedCost]
1016
+ required: [taskId, state, estimatedCost, deadlineAt]
867
1017
  properties:
868
1018
  taskId: { type: string }
1019
+ deadlineAt: { type: string, format: date-time, description: Server execution deadline fixed at acceptance; not a local wait timeout or result URL expiry. Idempotent replays retain the original deadline. }
869
1020
  state:
870
1021
  type: string
871
1022
  enum: [queued, running, succeeded, failed, canceled, expired]
@@ -941,7 +1092,7 @@ components:
941
1092
  type: string
942
1093
  enum: [queued, running, succeeded, failed, canceled, expired]
943
1094
  input:
944
- description: Normalized model input. Omitted after retention redaction.
1095
+ description: Normalized model input; inline images become account-bound file URIs. Omitted after retention redaction.
945
1096
  type: object
946
1097
  additionalProperties: true
947
1098
  output: { $ref: '#/components/schemas/TaskOutput' }
@@ -952,6 +1103,7 @@ components:
952
1103
  type: boolean
953
1104
  description: When false, `cost` is the held estimate. On success the final charge is capped at that hold; unused funds are released. Failed or expired tasks release the hold in full.
954
1105
  createdAt: { type: string, format: date-time }
1106
+ deadlineAt: { type: string, format: date-time, description: Server execution deadline. Present in current responses; historical stored webhook events may omit it. Not the result retention or URL expiry time. }
955
1107
  completedAt: { type: string, format: date-time }
956
1108
  TaskRecordEnvelope:
957
1109
  allOf:
@@ -976,10 +1128,89 @@ components:
976
1128
  error_message: { type: string }
977
1129
  cost: { $ref: '#/components/schemas/USDString' }
978
1130
  created_at: { type: string, format: date-time }
1131
+ APIKeyTaskItem:
1132
+ type: object
1133
+ additionalProperties: false
1134
+ required: [taskId, model, state, cost, settled, createdAt, deadlineAt]
1135
+ properties:
1136
+ taskId: { type: string }
1137
+ model: { type: string, description: Public model identifier. }
1138
+ state: { type: string, enum: [queued, running, succeeded, failed, canceled, expired] }
1139
+ cost:
1140
+ allOf:
1141
+ - $ref: '#/components/schemas/USDString'
1142
+ description: Final charged USD if settled; otherwise the current held estimate.
1143
+ settled: { type: boolean }
1144
+ createdAt: { type: string, format: date-time }
1145
+ deadlineAt: { type: string, format: date-time }
1146
+ completedAt: { type: string, format: date-time }
1147
+ APIKeyTaskListResponse:
1148
+ type: object
1149
+ additionalProperties: false
1150
+ required: [items, hasMore]
1151
+ properties:
1152
+ items:
1153
+ type: array
1154
+ items: { $ref: '#/components/schemas/APIKeyTaskItem' }
1155
+ hasMore: { type: boolean }
1156
+ nextCursor: { type: string, description: Present only when hasMore is true. }
1157
+ APIKeyTaskListEnvelope:
1158
+ allOf:
1159
+ - $ref: '#/components/schemas/EnvelopeBase'
1160
+ - type: object
1161
+ required: [data]
1162
+ properties:
1163
+ code: { type: integer, const: 200 }
1164
+ msg: { type: string, const: success }
1165
+ data: { $ref: '#/components/schemas/APIKeyTaskListResponse' }
1166
+ APIKeyUsageResponse:
1167
+ type: object
1168
+ additionalProperties: false
1169
+ required: [from, to, currency, totalCalls, totalSpend, days, models]
1170
+ properties:
1171
+ from: { type: string, format: date }
1172
+ to: { type: string, format: date }
1173
+ currency: { type: string, const: USD }
1174
+ totalCalls: { type: integer, format: int64, minimum: 0 }
1175
+ totalSpend: { $ref: '#/components/schemas/USDString' }
1176
+ days:
1177
+ type: array
1178
+ items: { $ref: '#/components/schemas/UsageDayItem' }
1179
+ models:
1180
+ type: array
1181
+ items: { $ref: '#/components/schemas/UsageModelItem' }
1182
+ UsageDayItem:
1183
+ type: object
1184
+ additionalProperties: false
1185
+ required: [day, calls, succeeded, failed, spend]
1186
+ properties:
1187
+ day: { type: string, format: date }
1188
+ calls: { type: integer, format: int64, minimum: 0 }
1189
+ succeeded: { type: integer, format: int64, minimum: 0 }
1190
+ failed: { type: integer, format: int64, minimum: 0 }
1191
+ spend: { $ref: '#/components/schemas/USDString' }
1192
+ UsageModelItem:
1193
+ type: object
1194
+ additionalProperties: false
1195
+ required: [model, calls, succeeded, failed, spend]
1196
+ properties:
1197
+ model: { type: string, description: Public model identifier. }
1198
+ calls: { type: integer, format: int64, minimum: 0 }
1199
+ succeeded: { type: integer, format: int64, minimum: 0 }
1200
+ failed: { type: integer, format: int64, minimum: 0 }
1201
+ spend: { $ref: '#/components/schemas/USDString' }
1202
+ APIKeyUsageEnvelope:
1203
+ allOf:
1204
+ - $ref: '#/components/schemas/EnvelopeBase'
1205
+ - type: object
1206
+ required: [data]
1207
+ properties:
1208
+ data: { $ref: '#/components/schemas/APIKeyUsageResponse' }
979
1209
  Balance:
980
1210
  type: object
981
1211
  required: [available, held, total]
982
1212
  properties:
1213
+ funding: { $ref: '#/components/schemas/FundingOverview' }
983
1214
  available: { $ref: '#/components/schemas/USDString' }
984
1215
  held: { $ref: '#/components/schemas/USDString' }
985
1216
  total: { $ref: '#/components/schemas/USDString' }