@sandstormapp/sdk 0.2.0 → 1.1.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
@@ -1,188 +1,137 @@
1
- # @sandstormapp/sdk
1
+ # ads.dev
2
2
 
3
- Sandstorm's public API as a typed client for Node and edge runtimes: one method per tool in the catalogue, typed from the tool's own JSON Schema. A tool that only reads answers its JSON at once; every other tool starts a task the client follows. It is generated from the API's OpenAPI document and depends on nothing at runtime but `fetch`.
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
- yarn add @sandstormapp/sdk
8
+ npm install ads.dev
7
9
  ```
8
10
 
9
11
  ```ts
10
- import { Sandstorm } from '@sandstormapp/sdk'
11
-
12
- const sandstorm = new Sandstorm({ apiKey: process.env.SANDSTORM_API_KEY })
12
+ import { Sandstorm } from 'ads.dev'
13
13
 
14
- const task = await sandstorm.tools.generate_image_nano_banana_pro(
15
- { prompt: 'A ceramic mug on a sunlit bench, steam rising, morning light', asset_name: 'Sunlit Mug' },
16
- { wait: 120 },
17
- )
18
- if (task.status === 'succeeded') console.log(task.files)
19
- ```
14
+ const sandstorm = new Sandstorm()
20
15
 
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
- Settings → API keys mints a key, `sk_live_…`, shown once. A key works on one brand of your organization: the brand it was made on, or the one you name.
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`:
32
26
 
33
- ```ts
34
- new Sandstorm({ apiKey, brand: '2f7fa2e7-c931-47b2-a66a-f1393233f2b3' })
27
+ ```bash
28
+ curl -fsSL https://ads.dev/login.mjs -o sandstorm-login.mjs && node sandstorm-login.mjs
35
29
  ```
36
30
 
37
- `brand` is sent as `x-sandstorm-brand` on every request. `baseUrl` points the client somewhere other than `https://agent.sandstormapp.com`, such as an API running locally.
38
-
39
- ## Reading
40
-
41
- A tool that only reads, such as `search_assets`, `read_offer` or `get_brand_identity`, is a `GET` with its arguments as query parameters, a list as its name repeated, and answers the tool's own JSON at once; the method takes the same arguments as an object:
42
-
43
31
  ```ts
44
- const { assets } = await sandstorm.tools.search_assets({ types: ['image'], limit: 10 })
45
- const identity = await sandstorm.tools.get_brand_identity()
46
- ```
47
-
48
- ```bash
49
- curl 'https://agent.sandstormapp.com/v1/tools/search_assets?types=image&limit=10' \
50
- -H "Authorization: Bearer $SANDSTORM_API_KEY"
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
+ })
51
39
  ```
52
40
 
53
- A read the tool refuses, such as a ref it cannot find, throws with status 422 and the tool's reason.
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.
54
42
 
55
- ## Calling a tool
43
+ ## Generations
56
44
 
57
- Every other tool is a method on `sandstorm.tools`, named as the catalogue names it, taking the body the API holds it to. A call starts a task and answers its id at once:
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.
58
46
 
59
47
  ```ts
60
- const started = await sandstorm.tools.generate_image_nano_banana_pro({ prompt, asset_name: 'Sunlit Mug' })
61
- // { task_id: '…', status: 'running' }
62
- ```
48
+ const still = await sandstorm.generations.run('nano-banana-2-lite', { prompt: 'Two lemons in a blue bowl' })
63
49
 
64
- ```bash
65
- curl -X POST https://agent.sandstormapp.com/v1/tools/generate_image_nano_banana_pro \
66
- -H "Authorization: Bearer $SANDSTORM_API_KEY" -H 'Content-Type: application/json' \
67
- -d '{"prompt":"…","asset_name":"Sunlit Mug"}'
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
+ })
68
55
  ```
69
56
 
70
- With `wait`, the API holds the request up to that many seconds (at most 200) and answers the finished task, or the started shape when the time runs out:
71
-
72
- ```ts
73
- const answer = await sandstorm.tools.generate_image_nano_banana_pro({ prompt, asset_name: 'Sunlit Mug' }, { wait: 30 })
74
- if (answer.status === 'running') console.log('still running', answer.task_id)
75
- else console.log(answer.files)
76
- ```
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.
77
58
 
78
- A tool that asks the person before it runs in the app takes `confirm: true` here, since nobody is there to ask; its input type requires it, and the API refuses the call with 403 without it:
59
+ For work that runs long, queue it and come back to it, or take a webhook:
79
60
 
80
61
  ```ts
81
- await sandstorm.tools.revoke_asset_share({ slug: 'summer-drop', confirm: true })
82
- ```
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)
83
64
 
84
- ```bash
85
- curl -X POST https://agent.sandstormapp.com/v1/tools/revoke_asset_share \
86
- -H "Authorization: Bearer $SANDSTORM_API_KEY" -H 'Content-Type: application/json' \
87
- -d '{"slug":"summer-drop","confirm":true}'
65
+ await sandstorm.generations.get(queued.id)
66
+ await sandstorm.generations.cancel(queued.id)
88
67
  ```
89
68
 
90
- The catalogue itself, every tool this key can call with its schema, hints and whether it asks first; `readOnlyHint` marks the tools that read, and `toolMethods`, exported beside the client, says which method each tool takes:
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:
91
72
 
92
73
  ```ts
93
- const { tools } = await sandstorm.catalogue()
74
+ await sandstorm.generations.create('nano-banana-pro', input, { idempotencyKey: 'campaign-42-hero' })
94
75
  ```
95
76
 
96
- ```bash
97
- curl https://agent.sandstormapp.com/v1/tools -H "Authorization: Bearer $SANDSTORM_API_KEY"
98
- ```
77
+ ## Lists
99
78
 
100
- ## Following a task
79
+ Every list answers one page when awaited and walks every page when iterated:
101
80
 
102
81
  ```ts
103
- const task = await sandstorm.tasks.get(taskId)
104
- ```
82
+ const page = await sandstorm.generations.list({ status: 'succeeded', limit: 50 })
105
83
 
106
- ```bash
107
- 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)
108
85
  ```
109
86
 
110
- A task is `{ task_id, tool, status, error, started_at, finished_at, result?, files? }`, where `status` is `running`, `succeeded`, `failed` or `cancelled`, `result` is the tool's own JSON once it succeeded and `files` are the files it made or showed, each with a `ref` other tools take and a `url` that keeps working. A task the API has only just started answers `{ task_id, status: 'running' }` until it is on record.
111
-
112
- `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
113
88
 
114
89
  ```ts
115
- const finished = await sandstorm.tasks.wait(taskId)
116
- if (finished.status === 'failed') console.error(finished.error)
117
- ```
90
+ await sandstorm.models.list({ type: 'video' })
91
+ await sandstorm.models.get('nano-banana-pro/edit')
118
92
 
119
- The twenty most recent calls of this key on its brand, newest first, without their results:
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 })
120
100
 
121
- ```ts
122
- const { tasks } = await sandstorm.tasks.list()
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')
123
104
  ```
124
105
 
125
- ```bash
126
- curl https://agent.sandstormapp.com/v1/tasks -H "Authorization: Bearer $SANDSTORM_API_KEY"
127
- ```
106
+ ## Webhooks
128
107
 
129
- Stopping one that still runs; whatever it saved before it stopped stays in the library:
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:
130
109
 
131
110
  ```ts
132
- await sandstorm.tasks.stop(taskId)
133
- ```
111
+ import { verifyWebhook } from 'ads.dev'
134
112
 
135
- ```bash
136
- curl -X POST https://agent.sandstormapp.com/v1/tasks/$TASK_ID/stop -H "Authorization: Bearer $SANDSTORM_API_KEY"
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)
137
117
  ```
138
118
 
139
- ## Refusals
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
140
122
 
141
- A response the API refuses throws a `SandstormError` with the HTTP `status`, the API's `code` and its message:
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.
142
124
 
143
125
  ```ts
144
- import { SandstormError } from '@sandstormapp/sdk'
126
+ import { SandstormError } from 'ads.dev'
145
127
 
146
128
  try {
147
- await sandstorm.tools.archive_offer({ offer: 'offer:1a2b3c4d' })
129
+ await sandstorm.generations.create('nano-banana-pro', { prompt: 'A mug on a bench' })
148
130
  } catch (error) {
149
- if (error instanceof SandstormError && error.code === 'forbidden') console.log(error.message)
131
+ if (error instanceof SandstormError && error.code === 'insufficient_credits') console.log('Add credits in Sandstorm')
150
132
  }
151
133
  ```
152
134
 
153
- | status | code | when |
154
- | --- | --- | --- |
155
- | 400 | `bad_request` | the query or body does not match the tool's schema; the message names the mismatch |
156
- | 401 | `unauthorized` | the bearer is not a live key |
157
- | 402 | `billing` | the organization cannot run Sandstorm right now |
158
- | 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 |
159
- | 404 | `tool_not_found`, `task_not_found` | no such tool for this brand, or no such task |
160
- | 405 | `method_not_allowed` | a read was posted, or a task tool was read; the message names the method that works |
161
- | 409 | `task_not_running` | the task to stop has already ended |
162
- | 422 | `tool_failed` | a read the tool refused, with the tool's reason |
163
- | 429 | `rate_limited` | more than 300 requests in a minute from this key |
164
-
165
- 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.
166
-
167
- ## The generated client
168
-
169
- `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:
170
-
171
- ```bash
172
- yarn generate https://agent.sandstormapp.com/v1/openapi.json # from a running API, replacing openapi.json
173
- yarn generate path/to/openapi.json # from a document on disk
174
- yarn generate # from the committed openapi.json
175
- ```
176
-
177
- `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`.
178
-
179
135
  ## Developing
180
136
 
181
- ```bash
182
- yarn install
183
- yarn typecheck # src, test and scripts
184
- yarn test # the client against a stubbed fetch, and the stale-output check
185
- yarn build # dist/, which is what the package ships
186
- ```
187
-
188
- 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,52 +1,139 @@
1
- import { toolMethods, type components, type paths } from './openapi.ts';
2
- export { toolMethods };
3
- export type Task = components['schemas']['Task'];
4
- export type Started = components['schemas']['Started'];
5
- export type Tool = components['schemas']['Tool'];
6
- export type Finished = Task & {
7
- readonly status: Exclude<Task['status'], 'running'>;
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 ToolName = keyof typeof toolMethods;
10
- type Method<Name extends ToolName> = (typeof toolMethods)[Name];
11
- type Operation<Name extends ToolName> = NonNullable<paths[`/v1/tools/${Name}`][Method<Name>]>;
12
- type Query<Name extends ToolName> = Operation<Name>['parameters']['query'];
13
- export type ToolInput<Name extends ToolName> = Method<Name> extends 'get' ? NonNullable<Query<Name>> : NonNullable<Operation<Name>['requestBody']>['content']['application/json'];
14
- export type ReadResult<Name extends ToolName> = Operation<Name>['responses'][200]['content']['application/json'];
15
- export type Catalogue = paths['/v1/tools']['get']['responses'][200]['content']['application/json'];
16
- export type TaskList = paths['/v1/tasks']['get']['responses'][200]['content']['application/json'];
17
- export type Stopped = paths['/v1/tasks/{taskId}/stop']['post']['responses'][202]['content']['application/json'];
18
- export interface Wait {
19
- readonly wait: number;
20
- }
21
- export interface ToolCall<Name extends ToolName> {
22
- (input: ToolInput<Name>): Promise<Started>;
23
- (input: ToolInput<Name>, options: Wait): Promise<Task | Started>;
24
- }
25
- export type ToolRead<Name extends ToolName> = undefined extends Query<Name> ? (query?: ToolInput<Name>) => Promise<ReadResult<Name>> : (query: ToolInput<Name>) => Promise<ReadResult<Name>>;
26
- export type Tools = {
27
- readonly [Name in ToolName]: Method<Name> extends 'get' ? ToolRead<Name> : 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;
28
35
  };
29
- export interface Tasks {
30
- list(): Promise<TaskList>;
31
- get(taskId: string): Promise<Task | Started>;
32
- stop(taskId: string): Promise<Stopped>;
33
- wait(taskId: string): Promise<Finished>;
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;
34
42
  }
35
43
  export interface Options {
36
- readonly apiKey: string;
37
- readonly brand?: string;
44
+ readonly apiKey?: string;
38
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;
39
58
  }
40
59
  export declare class SandstormError extends Error {
41
60
  readonly name = "SandstormError";
42
61
  readonly status: number;
43
- readonly code: string | undefined;
44
- constructor(status: number, code: string | undefined, message: string);
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>;
45
81
  }
46
82
  export declare class Sandstorm {
47
83
  #private;
48
- readonly tools: Tools;
49
- readonly tasks: Tasks;
50
- constructor({ apiKey, brand, baseUrl }: Options);
51
- catalogue(): Promise<Catalogue>;
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;
52
137
  }
138
+ export declare function verifyWebhook({ secret, body, signature, toleranceSeconds, now }: Delivery): Promise<GenerationEvent>;
139
+ export {};