@sandstormapp/sdk 0.1.0 → 1.0.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 +75 -108
- package/dist/index.d.ts +126 -34
- package/dist/index.js +174 -50
- package/dist/openapi.d.ts +2414 -14087
- package/dist/openapi.js +1 -123
- package/openapi.json +5628 -18620
- package/package.json +13 -2
package/README.md
CHANGED
|
@@ -1,170 +1,137 @@
|
|
|
1
|
-
#
|
|
1
|
+
# ads.dev
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
The [ads.dev](https://ads.dev) API for Node, Deno, Bun and edge runtimes: image, video and audio generation with 34 models on your brand, the brand kit and library, and the ads your competitors run. Every model's input is typed, so the model you name decides what `input` takes.
|
|
4
|
+
|
|
5
|
+
It is published as `ads.dev`, `adsdotdev`, `sandstormapp` and `@sandstormapp/sdk`, the same package under four names, and needs nothing at runtime but `fetch` and Web Crypto.
|
|
4
6
|
|
|
5
7
|
```bash
|
|
6
|
-
|
|
8
|
+
npm install ads.dev
|
|
7
9
|
```
|
|
8
10
|
|
|
9
11
|
```ts
|
|
10
|
-
import { Sandstorm } from '
|
|
12
|
+
import { Sandstorm } from 'ads.dev'
|
|
11
13
|
|
|
12
|
-
const sandstorm = new Sandstorm(
|
|
14
|
+
const sandstorm = new Sandstorm()
|
|
13
15
|
|
|
14
|
-
const
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
)
|
|
18
|
-
|
|
19
|
-
```
|
|
20
|
-
|
|
21
|
-
```bash
|
|
22
|
-
curl -X POST https://agent.sandstormapp.com/v1/tools/generate_image_nano_banana_pro \
|
|
23
|
-
-H "Authorization: Bearer $SANDSTORM_API_KEY" \
|
|
24
|
-
-H 'Content-Type: application/json' \
|
|
25
|
-
-H 'Prefer: wait=120' \
|
|
26
|
-
-d '{"prompt":"A ceramic mug on a sunlit bench, steam rising, morning light","asset_name":"Sunlit Mug"}'
|
|
16
|
+
const generation = await sandstorm.generations.run('nano-banana-pro', {
|
|
17
|
+
prompt: 'A ceramic mug on a sunlit bench, steam rising',
|
|
18
|
+
aspect_ratio: '4:5',
|
|
19
|
+
})
|
|
20
|
+
console.log(generation.output[0]?.url)
|
|
27
21
|
```
|
|
28
22
|
|
|
29
23
|
## A key
|
|
30
24
|
|
|
31
|
-
|
|
25
|
+
`new Sandstorm()` reads `SANDSTORM_API_KEY`. Make a key in Sandstorm under Settings → API keys, or in one command, which signs you in in the browser and writes the key to `.env`:
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
curl -fsSL https://ads.dev/login.mjs -o sandstorm-login.mjs && node sandstorm-login.mjs
|
|
29
|
+
```
|
|
32
30
|
|
|
33
31
|
```ts
|
|
34
|
-
new Sandstorm({
|
|
32
|
+
new Sandstorm({
|
|
33
|
+
apiKey: process.env.SANDSTORM_API_KEY,
|
|
34
|
+
brand: '6f1c2d3e-4b5a-4c7d-8e9f-0a1b2c3d4e5f',
|
|
35
|
+
baseUrl: 'https://ads.dev',
|
|
36
|
+
maxRetries: 2,
|
|
37
|
+
timeoutMs: 60_000,
|
|
38
|
+
})
|
|
35
39
|
```
|
|
36
40
|
|
|
37
|
-
|
|
41
|
+
A key acts on the brand it was made on; `brand` names another brand of the same organization. `baseUrl` defaults to `SANDSTORM_API_URL`, then `https://ads.dev`. Keep the key on your server: anyone who holds it spends your credits.
|
|
38
42
|
|
|
39
|
-
##
|
|
43
|
+
## Generations
|
|
40
44
|
|
|
41
|
-
|
|
45
|
+
`run` makes a generation and answers it once it has finished, holding each request on the API with `Prefer: wait` rather than polling. A generation that fails or is canceled throws `GenerationFailed`, which carries it.
|
|
42
46
|
|
|
43
47
|
```ts
|
|
44
|
-
const
|
|
45
|
-
// { task_id: '…', status: 'running' }
|
|
46
|
-
```
|
|
48
|
+
const still = await sandstorm.generations.run('nano-banana-2-lite', { prompt: 'Two lemons in a blue bowl' })
|
|
47
49
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
50
|
+
const clip = await sandstorm.generations.run('seedance-2.5/image-to-video', {
|
|
51
|
+
start_image: still.output[0]!.id,
|
|
52
|
+
prompt: 'Slow push in as the light warms',
|
|
53
|
+
resolution: '720p',
|
|
54
|
+
})
|
|
52
55
|
```
|
|
53
56
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
```ts
|
|
57
|
-
const answer = await sandstorm.tools.search_assets({ types: ['image'], limit: 10 }, { wait: 30 })
|
|
58
|
-
if (answer.status === 'running') console.log('still running', answer.task_id)
|
|
59
|
-
else console.log(answer.result)
|
|
60
|
-
```
|
|
57
|
+
Every media input takes an asset id (`asset_…`) or a public https URL, so the output of one generation goes straight into the next.
|
|
61
58
|
|
|
62
|
-
|
|
59
|
+
For work that runs long, queue it and come back to it, or take a webhook:
|
|
63
60
|
|
|
64
61
|
```ts
|
|
65
|
-
await sandstorm.
|
|
66
|
-
|
|
62
|
+
const queued = await sandstorm.generations.create('kling-3-pro/image-to-video', input, { webhookUrl: 'https://yourapp.com/hooks/sandstorm' })
|
|
63
|
+
const finished = await sandstorm.generations.wait(queued.id)
|
|
67
64
|
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
-H "Authorization: Bearer $SANDSTORM_API_KEY" -H 'Content-Type: application/json' \
|
|
71
|
-
-d '{"slug":"summer-drop","confirm":true}'
|
|
65
|
+
await sandstorm.generations.get(queued.id)
|
|
66
|
+
await sandstorm.generations.cancel(queued.id)
|
|
72
67
|
```
|
|
73
68
|
|
|
74
|
-
|
|
69
|
+
A generation is `queued`, then `running`, then `succeeded`, `failed` or `canceled`; `credits` is what it was charged once it finished, and a failed one is free. Cancelling a queued generation costs nothing; one the model has already started stops where the model allows it, and otherwise finishes and is charged.
|
|
70
|
+
|
|
71
|
+
`create` sends an `Idempotency-Key` with every call and keeps it across its retries, so a retry never makes a second generation. Pass your own to make a call safe across process restarts:
|
|
75
72
|
|
|
76
73
|
```ts
|
|
77
|
-
|
|
74
|
+
await sandstorm.generations.create('nano-banana-pro', input, { idempotencyKey: 'campaign-42-hero' })
|
|
78
75
|
```
|
|
79
76
|
|
|
80
|
-
|
|
81
|
-
curl https://agent.sandstormapp.com/v1/tools -H "Authorization: Bearer $SANDSTORM_API_KEY"
|
|
82
|
-
```
|
|
77
|
+
## Lists
|
|
83
78
|
|
|
84
|
-
|
|
79
|
+
Every list answers one page when awaited and walks every page when iterated:
|
|
85
80
|
|
|
86
81
|
```ts
|
|
87
|
-
const
|
|
88
|
-
```
|
|
82
|
+
const page = await sandstorm.generations.list({ status: 'succeeded', limit: 50 })
|
|
89
83
|
|
|
90
|
-
|
|
91
|
-
curl https://agent.sandstormapp.com/v1/tasks/$TASK_ID -H "Authorization: Bearer $SANDSTORM_API_KEY"
|
|
84
|
+
for await (const asset of sandstorm.assets.list({ type: 'image' })) console.log(asset.id, asset.url)
|
|
92
85
|
```
|
|
93
86
|
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
`tasks.wait` polls that route every two seconds until the status leaves `running`, and resolves to the task with its result or error:
|
|
87
|
+
## Everything else
|
|
97
88
|
|
|
98
89
|
```ts
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
```
|
|
90
|
+
await sandstorm.models.list({ type: 'video' })
|
|
91
|
+
await sandstorm.models.get('nano-banana-pro/edit')
|
|
102
92
|
|
|
103
|
-
|
|
93
|
+
await sandstorm.brand.get()
|
|
94
|
+
await sandstorm.assets.import('https://yourcdn.com/hero.png', { name: 'Hero shot' })
|
|
95
|
+
await sandstorm.assets.get('asset_9c1e77aa')
|
|
96
|
+
await sandstorm.offers.list({ query: 'cold brew' })
|
|
97
|
+
await sandstorm.offers.get('offer_0f8e2b6a4c1d4e7f9a3b5c7d9e1f2a4b')
|
|
98
|
+
await sandstorm.characters.list({ kind: 'subject' })
|
|
99
|
+
await sandstorm.voices.list({ use_case: 'advertisement', limit: 5 })
|
|
104
100
|
|
|
105
|
-
|
|
106
|
-
|
|
101
|
+
const [advertiser] = (await sandstorm.advertisers.list({ query: 'Liquid Death' })).data
|
|
102
|
+
await sandstorm.ads.list({ advertiser: advertiser!.id, status: 'active' })
|
|
103
|
+
await sandstorm.ads.get('ad_5e1c9a3b')
|
|
107
104
|
```
|
|
108
105
|
|
|
109
|
-
|
|
110
|
-
curl https://agent.sandstormapp.com/v1/tasks -H "Authorization: Bearer $SANDSTORM_API_KEY"
|
|
111
|
-
```
|
|
106
|
+
## Webhooks
|
|
112
107
|
|
|
113
|
-
|
|
108
|
+
A generation made with `webhookUrl` is POSTed to it once it finishes, signed in `Sandstorm-Signature`. Verify the raw body before you trust it:
|
|
114
109
|
|
|
115
110
|
```ts
|
|
116
|
-
|
|
117
|
-
```
|
|
111
|
+
import { verifyWebhook } from 'ads.dev'
|
|
118
112
|
|
|
119
|
-
|
|
120
|
-
|
|
113
|
+
const secret = await sandstorm.webhooks.secret()
|
|
114
|
+
|
|
115
|
+
const event = await verifyWebhook({ secret, body: await request.text(), signature: request.headers.get('sandstorm-signature') })
|
|
116
|
+
if (event.type === 'generation.succeeded') console.log(event.data.output)
|
|
121
117
|
```
|
|
122
118
|
|
|
123
|
-
|
|
119
|
+
It refuses a body that was changed, a signature from another secret and a delivery older than five minutes. An event's `id` is the same on every retry of it, so a handler can skip one it has seen.
|
|
120
|
+
|
|
121
|
+
## Errors
|
|
124
122
|
|
|
125
|
-
A
|
|
123
|
+
A refusal throws `SandstormError` with the HTTP `status`, its `type` and `code`, the `param` at fault and the `requestId` to quote to support. Requests Sandstorm could not take (408, 429, 500, 502, 503, 504) and network failures are retried twice with backoff, after the `Retry-After` the API asks for.
|
|
126
124
|
|
|
127
125
|
```ts
|
|
128
|
-
import { SandstormError } from '
|
|
126
|
+
import { SandstormError } from 'ads.dev'
|
|
129
127
|
|
|
130
128
|
try {
|
|
131
|
-
await sandstorm.
|
|
129
|
+
await sandstorm.generations.create('nano-banana-pro', { prompt: 'A mug on a bench' })
|
|
132
130
|
} catch (error) {
|
|
133
|
-
if (error instanceof SandstormError && error.code === '
|
|
131
|
+
if (error instanceof SandstormError && error.code === 'insufficient_credits') console.log('Add credits in Sandstorm')
|
|
134
132
|
}
|
|
135
133
|
```
|
|
136
134
|
|
|
137
|
-
| status | code | when |
|
|
138
|
-
| --- | --- | --- |
|
|
139
|
-
| 400 | `bad_request` | the body does not match the tool's schema; the message names the mismatch |
|
|
140
|
-
| 401 | `unauthorized` | the bearer is not a live key |
|
|
141
|
-
| 402 | `billing` | the organization cannot run Sandstorm right now |
|
|
142
|
-
| 403 | `forbidden` | the tool asks first and the body did not carry `confirm: true`, or the brand named is not one of the organization's |
|
|
143
|
-
| 404 | `tool_not_found`, `task_not_found` | no such tool for this brand, or no such task |
|
|
144
|
-
| 409 | `task_not_running` | the task to stop has already ended |
|
|
145
|
-
| 429 | `rate_limited` | more than 300 requests in a minute from this key |
|
|
146
|
-
|
|
147
|
-
An answer that is not the API's, such as one from the edge, throws with its `status`, no `code`, and the text of the answer.
|
|
148
|
-
|
|
149
|
-
## The generated client
|
|
150
|
-
|
|
151
|
-
`src/openapi.ts` and `openapi.json` are generated from the API's `GET /v1/openapi.json`, which the API produces from its live tool catalogue, and both are committed, so the package builds without an API. To regenerate:
|
|
152
|
-
|
|
153
|
-
```bash
|
|
154
|
-
yarn generate https://agent.sandstormapp.com/v1/openapi.json # from a running API, replacing openapi.json
|
|
155
|
-
yarn generate path/to/openapi.json # from a document on disk
|
|
156
|
-
yarn generate # from the committed openapi.json
|
|
157
|
-
```
|
|
158
|
-
|
|
159
|
-
`yarn test` fails when the committed `src/openapi.ts` is not what the committed document generates, and a document regenerated from the API shows every catalogue change as a diff of `openapi.json`. `agent/README.md` says how to run the API locally; its document is at `http://localhost:8787/v1/openapi.json`.
|
|
160
|
-
|
|
161
135
|
## Developing
|
|
162
136
|
|
|
163
|
-
|
|
164
|
-
yarn install
|
|
165
|
-
yarn typecheck # src, test and scripts
|
|
166
|
-
yarn test # the client against a stubbed fetch, and the stale-output check
|
|
167
|
-
yarn build # dist/, which is what the package ships
|
|
168
|
-
```
|
|
169
|
-
|
|
170
|
-
Node 22.18 or newer runs `scripts/generate.ts` as it is.
|
|
137
|
+
`openapi.json` is the API's own document, held to what the API serves by the agent project's tests; `yarn generate` turns it into `src/openapi.ts`, and `yarn ci:test` fails when the two disagree. `yarn release` publishes the build as `ads.dev`, `adsdotdev`, `sandstormapp` and `@sandstormapp/sdk`.
|
package/dist/index.d.ts
CHANGED
|
@@ -1,47 +1,139 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
export type
|
|
4
|
-
export type
|
|
5
|
-
export type
|
|
6
|
-
export type
|
|
7
|
-
|
|
1
|
+
import type { components, operations } from './openapi.ts';
|
|
2
|
+
type Schemas = components['schemas'];
|
|
3
|
+
export type Generation = Schemas['Generation'];
|
|
4
|
+
export type GenerationEvent = Schemas['GenerationEvent'];
|
|
5
|
+
export type Model = Schemas['Model'];
|
|
6
|
+
export type Asset = Schemas['Asset'];
|
|
7
|
+
export type LibraryAsset = Schemas['LibraryAsset'];
|
|
8
|
+
export type Brand = Schemas['Brand'];
|
|
9
|
+
export type OfferSummary = Schemas['OfferSummary'];
|
|
10
|
+
export type Offer = Schemas['Offer'];
|
|
11
|
+
export type Character = Schemas['Character'];
|
|
12
|
+
export type Voice = Schemas['Voice'];
|
|
13
|
+
export type Advertiser = Schemas['Advertiser'];
|
|
14
|
+
export type AdSummary = Schemas['AdSummary'];
|
|
15
|
+
export type Ad = Schemas['Ad'];
|
|
16
|
+
type CreateGeneration = Schemas['CreateGeneration'];
|
|
17
|
+
type Refusal = Schemas['Error']['error'];
|
|
18
|
+
export type ModelId = CreateGeneration['model'];
|
|
19
|
+
export type ModelInput<Id extends ModelId> = Extract<CreateGeneration, {
|
|
20
|
+
model: Id;
|
|
21
|
+
}>['input'];
|
|
22
|
+
type Query<Operation extends keyof operations> = NonNullable<operations[Operation]['parameters']['query']>;
|
|
23
|
+
type Flag<Value> = [Value] extends ['true' | 'false' | undefined] ? boolean : Value;
|
|
24
|
+
type Params<Operation extends keyof operations> = {
|
|
25
|
+
readonly [Name in Exclude<keyof Query<Operation>, 'cursor'>]?: Flag<Query<Operation>[Name]>;
|
|
8
26
|
};
|
|
9
|
-
export type
|
|
10
|
-
export type
|
|
11
|
-
export type
|
|
12
|
-
export type
|
|
13
|
-
export type
|
|
14
|
-
export
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
export interface ToolCall<Name extends ToolName> {
|
|
18
|
-
(input: ToolInput<Name>): Promise<Started>;
|
|
19
|
-
(input: ToolInput<Name>, options: Wait): Promise<Task | Started>;
|
|
20
|
-
}
|
|
21
|
-
export type Tools = {
|
|
22
|
-
readonly [Name in ToolName]: ToolCall<Name>;
|
|
27
|
+
export type GenerationParams = Params<'listGenerations'>;
|
|
28
|
+
export type ModelParams = Params<'listModels'>;
|
|
29
|
+
export type AssetParams = Params<'listAssets'>;
|
|
30
|
+
export type OfferParams = Params<'listOffers'>;
|
|
31
|
+
export type CharacterParams = Params<'listCharacters'>;
|
|
32
|
+
export type VoiceParams = Params<'listVoices'>;
|
|
33
|
+
export type AdvertiserParams = Params<'listAdvertisers'> & {
|
|
34
|
+
readonly query: string;
|
|
23
35
|
};
|
|
24
|
-
export
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
36
|
+
export type AdParams = Params<'listAds'>;
|
|
37
|
+
export interface List<Item> {
|
|
38
|
+
readonly object: 'list';
|
|
39
|
+
readonly data: Item[];
|
|
40
|
+
readonly has_more: boolean;
|
|
41
|
+
readonly next_cursor: string | null;
|
|
29
42
|
}
|
|
30
43
|
export interface Options {
|
|
31
|
-
readonly apiKey
|
|
32
|
-
readonly brand?: string;
|
|
44
|
+
readonly apiKey?: string;
|
|
33
45
|
readonly baseUrl?: string;
|
|
46
|
+
readonly brand?: string;
|
|
47
|
+
readonly maxRetries?: number;
|
|
48
|
+
readonly timeoutMs?: number;
|
|
49
|
+
readonly fetch?: typeof fetch;
|
|
50
|
+
}
|
|
51
|
+
export interface CreateOptions {
|
|
52
|
+
readonly webhookUrl?: string;
|
|
53
|
+
readonly wait?: number;
|
|
54
|
+
readonly idempotencyKey?: string;
|
|
55
|
+
}
|
|
56
|
+
export interface WaitOptions {
|
|
57
|
+
readonly timeoutMs?: number;
|
|
34
58
|
}
|
|
35
59
|
export declare class SandstormError extends Error {
|
|
36
60
|
readonly name = "SandstormError";
|
|
37
61
|
readonly status: number;
|
|
38
|
-
readonly
|
|
39
|
-
|
|
62
|
+
readonly type: Refusal['type'];
|
|
63
|
+
readonly code: string;
|
|
64
|
+
readonly param: string | undefined;
|
|
65
|
+
readonly requestId: string | undefined;
|
|
66
|
+
constructor(status: number, refusal: Omit<Refusal, 'request_id'> & {
|
|
67
|
+
readonly request_id?: string;
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
export declare class GenerationFailed extends Error {
|
|
71
|
+
readonly name = "GenerationFailed";
|
|
72
|
+
readonly generation: Generation;
|
|
73
|
+
constructor(generation: Generation);
|
|
74
|
+
}
|
|
75
|
+
export declare const isFinished: (generation: Generation) => boolean;
|
|
76
|
+
export declare class Page<Item> implements PromiseLike<List<Item>>, AsyncIterable<Item> {
|
|
77
|
+
#private;
|
|
78
|
+
constructor(read: (cursor: string | undefined) => Promise<List<Item>>);
|
|
79
|
+
then<Fulfilled = List<Item>, Rejected = never>(fulfilled?: ((list: List<Item>) => Fulfilled | PromiseLike<Fulfilled>) | null, rejected?: ((reason: unknown) => Rejected | PromiseLike<Rejected>) | null): Promise<Fulfilled | Rejected>;
|
|
80
|
+
[Symbol.asyncIterator](): AsyncIterator<Item>;
|
|
40
81
|
}
|
|
41
82
|
export declare class Sandstorm {
|
|
42
83
|
#private;
|
|
43
|
-
|
|
44
|
-
readonly
|
|
45
|
-
|
|
46
|
-
|
|
84
|
+
constructor({ apiKey, baseUrl, brand, maxRetries, timeoutMs, fetch: fetcher, }?: Options);
|
|
85
|
+
readonly generations: {
|
|
86
|
+
create: <Id extends ModelId>(model: Id, input: ModelInput<Id>, { webhookUrl, wait, idempotencyKey }?: CreateOptions) => Promise<Generation>;
|
|
87
|
+
get: (id: string, { wait }?: {
|
|
88
|
+
readonly wait?: number;
|
|
89
|
+
}) => Promise<Generation>;
|
|
90
|
+
wait: (id: string, { timeoutMs }?: WaitOptions) => Promise<Generation>;
|
|
91
|
+
run: <Id extends ModelId>(model: Id, input: ModelInput<Id>, { timeoutMs, ...options }?: Omit<CreateOptions, "wait"> & WaitOptions) => Promise<Generation>;
|
|
92
|
+
cancel: (id: string) => Promise<Generation>;
|
|
93
|
+
list: (params?: GenerationParams) => Page<Generation>;
|
|
94
|
+
};
|
|
95
|
+
readonly models: {
|
|
96
|
+
list: (params?: ModelParams) => Page<Model>;
|
|
97
|
+
get: (id: ModelId) => Promise<Model>;
|
|
98
|
+
};
|
|
99
|
+
readonly assets: {
|
|
100
|
+
list: (params?: AssetParams) => Page<LibraryAsset>;
|
|
101
|
+
get: (id: string) => Promise<Asset>;
|
|
102
|
+
import: (url: string, { name, idempotencyKey }?: {
|
|
103
|
+
readonly name?: string;
|
|
104
|
+
readonly idempotencyKey?: string;
|
|
105
|
+
}) => Promise<Asset>;
|
|
106
|
+
};
|
|
107
|
+
readonly brand: {
|
|
108
|
+
get: () => Promise<Brand>;
|
|
109
|
+
};
|
|
110
|
+
readonly offers: {
|
|
111
|
+
list: (params?: OfferParams) => Page<OfferSummary>;
|
|
112
|
+
get: (id: string) => Promise<Offer>;
|
|
113
|
+
};
|
|
114
|
+
readonly characters: {
|
|
115
|
+
list: (params?: CharacterParams) => Page<Character>;
|
|
116
|
+
};
|
|
117
|
+
readonly voices: {
|
|
118
|
+
list: (params?: VoiceParams) => Page<Voice>;
|
|
119
|
+
};
|
|
120
|
+
readonly advertisers: {
|
|
121
|
+
list: (params: AdvertiserParams) => Page<Advertiser>;
|
|
122
|
+
};
|
|
123
|
+
readonly ads: {
|
|
124
|
+
list: (params?: AdParams) => Page<AdSummary>;
|
|
125
|
+
get: (id: string) => Promise<Ad>;
|
|
126
|
+
};
|
|
127
|
+
readonly webhooks: {
|
|
128
|
+
secret: () => Promise<string>;
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
export interface Delivery {
|
|
132
|
+
readonly secret: string;
|
|
133
|
+
readonly body: string;
|
|
134
|
+
readonly signature: string | null;
|
|
135
|
+
readonly toleranceSeconds?: number;
|
|
136
|
+
readonly now?: number;
|
|
47
137
|
}
|
|
138
|
+
export declare function verifyWebhook({ secret, body, signature, toleranceSeconds, now }: Delivery): Promise<GenerationEvent>;
|
|
139
|
+
export {};
|