@aexhq/sdk 0.40.6 → 0.40.8
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 +159 -159
- package/dist/_contracts/asset-upload-helper.d.ts +38 -0
- package/dist/_contracts/asset-upload-helper.js +111 -0
- package/dist/_contracts/internal.d.ts +5 -9
- package/dist/_contracts/internal.js +5 -79
- package/dist/_contracts/operations.d.ts +9 -2
- package/dist/_contracts/operations.js +141 -16
- package/dist/_contracts/retry-core.d.ts +27 -0
- package/dist/_contracts/retry-core.js +75 -0
- package/dist/_contracts/runtime-types.d.ts +6 -0
- package/dist/_contracts/stable.d.ts +10 -5
- package/dist/_contracts/stable.js +10 -5
- package/dist/asset-upload.d.ts +2 -1
- package/dist/asset-upload.js +28 -61
- package/dist/asset-upload.js.map +1 -1
- package/dist/cli.mjs +297 -80
- package/dist/cli.mjs.sha256 +1 -1
- package/dist/client.d.ts +5 -0
- package/dist/client.js +8 -13
- package/dist/client.js.map +1 -1
- package/dist/retry.js +13 -41
- package/dist/retry.js.map +1 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/docs/authentication.md +125 -125
- package/docs/billing.md +118 -118
- package/docs/concepts/agent-tools.md +66 -66
- package/docs/concepts/composition.md +37 -37
- package/docs/concepts/providers-and-runtimes.md +54 -54
- package/docs/concepts/runs.md +49 -49
- package/docs/concepts/subagents.md +88 -88
- package/docs/credentials.md +125 -125
- package/docs/defaults.md +64 -64
- package/docs/errors.md +196 -196
- package/docs/events.md +243 -243
- package/docs/limits-and-quotas.md +144 -144
- package/docs/limits.md +50 -50
- package/docs/mcp.md +44 -44
- package/docs/networking.md +163 -163
- package/docs/outputs.md +267 -267
- package/docs/public-surface.json +77 -77
- package/docs/quickstart.md +119 -119
- package/docs/release.md +38 -38
- package/docs/retries.md +129 -129
- package/docs/run-config.md +58 -58
- package/docs/run-record.md +55 -55
- package/docs/secrets.md +125 -125
- package/docs/testing.md +35 -35
- package/docs/vision-skills.md +91 -91
- package/docs/webhooks.md +127 -127
- package/examples/feature-tour.ts +282 -282
- package/examples/spike-settle-latency.ts +125 -125
- package/package.json +1 -1
package/docs/webhooks.md
CHANGED
|
@@ -1,128 +1,128 @@
|
|
|
1
|
-
---
|
|
2
|
-
title: Webhooks
|
|
3
|
-
---
|
|
4
|
-
|
|
5
|
-
# Webhooks
|
|
6
|
-
|
|
7
|
-
aex can notify your endpoint when a run finishes. Webhooks are **per-run**: you
|
|
8
|
-
pass a callback URL with the submission, and the platform delivers exactly one
|
|
9
|
-
`run.finished` event to it when the run reaches its terminal state.
|
|
10
|
-
|
|
1
|
+
---
|
|
2
|
+
title: Webhooks
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
# Webhooks
|
|
6
|
+
|
|
7
|
+
aex can notify your endpoint when a run finishes. Webhooks are **per-run**: you
|
|
8
|
+
pass a callback URL with the submission, and the platform delivers exactly one
|
|
9
|
+
`run.finished` event to it when the run reaches its terminal state.
|
|
10
|
+
|
|
11
11
|
## Register a callback
|
|
12
|
-
|
|
13
|
-
```ts
|
|
14
|
-
import { Aex, Models } from "@aexhq/sdk";
|
|
15
|
-
|
|
16
|
-
const aex = new Aex(process.env.AEX_API_KEY!);
|
|
17
|
-
|
|
18
|
-
const session = await aex.openSession({
|
|
19
|
-
model: Models.CLAUDE_HAIKU_4_5,
|
|
20
|
-
webhook: { url: "https://hooks.example.com/aex" },
|
|
21
|
-
apiKeys: { anthropic: process.env.ANTHROPIC_API_KEY! }
|
|
22
|
-
});
|
|
23
|
-
```
|
|
24
|
-
|
|
25
|
-
```bash
|
|
26
|
-
aex run \
|
|
27
|
-
--api-key "$AEX_API_KEY" \
|
|
28
|
-
--anthropic-api-key "$ANTHROPIC_API_KEY" \
|
|
29
|
-
--model claude-haiku-4-5 \
|
|
30
|
-
--prompt "Write the report." \
|
|
31
|
-
--webhook https://hooks.example.com/aex
|
|
32
|
-
```
|
|
33
|
-
|
|
34
|
-
The URL must be `https`. The callback URL is an operational concern, not part
|
|
35
|
-
of the submission fingerprint: retrying the same submission with the same
|
|
36
|
-
idempotency key but a different callback URL never conflicts.
|
|
37
|
-
|
|
38
|
-
## What gets delivered
|
|
39
|
-
|
|
40
|
-
One POST carrying the terminal `run.finished` event, sent at the
|
|
41
|
-
settle-consistent barrier — when you receive it, the run record is already
|
|
42
|
-
terminal and its outputs are complete and readable. The body is built once at
|
|
43
|
-
settle, frozen, and re-sent byte-identical on every retry or manual redelivery:
|
|
44
|
-
|
|
45
|
-
```json
|
|
46
|
-
{
|
|
47
|
-
"specversion": "1.0",
|
|
48
|
-
"id": "<delivery id — matches the webhook-id header>",
|
|
49
|
-
"source": "aex",
|
|
50
|
-
"type": "run.finished",
|
|
51
|
-
"subject": "<the run id>",
|
|
52
|
-
"time": "2026-07-02T12:34:56.000Z",
|
|
53
|
-
"data": {
|
|
54
|
-
"runId": "<the run id>",
|
|
55
|
-
"status": "succeeded",
|
|
56
|
-
"terminalAt": "2026-07-02T12:34:56.000Z",
|
|
57
|
-
"reason": null,
|
|
58
|
-
"failureClass": null,
|
|
59
|
-
"costTelemetry": { "billedCostUsd": 0.42 }
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
```
|
|
63
|
-
|
|
64
|
-
`reason` / `failureClass` carry the failure detail on non-success terminals;
|
|
65
|
-
`costTelemetry` is present when the billed cost is known.
|
|
66
|
-
|
|
67
|
-
## Verify deliveries
|
|
68
|
-
|
|
69
|
-
Deliveries are signed [Standard Webhooks](https://www.standardwebhooks.com/)
|
|
70
|
-
style: HMAC-SHA256 over `` `${webhook-id}.${webhook-timestamp}.${rawBody}` ``,
|
|
71
|
-
sent in three headers — `webhook-id` (stable across retries; your dedupe key),
|
|
72
|
-
`webhook-timestamp` (unix seconds), and `webhook-signature` (a space-delimited
|
|
73
|
-
list of `v1,<base64>` entries).
|
|
74
|
-
|
|
75
|
-
The signing key is a per-workspace secret. Reveal it (it is created on first
|
|
76
|
-
use) with either surface:
|
|
77
|
-
|
|
78
|
-
```bash
|
|
79
|
-
aex webhooks secret --api-key "$AEX_API_KEY" # prints whsec_...
|
|
80
|
-
```
|
|
81
|
-
|
|
82
|
-
```ts
|
|
83
|
-
const { whsec } = await aex.webhookSigningSecret();
|
|
84
|
-
```
|
|
85
|
-
|
|
86
|
-
Verify inbound requests with the exported `verifyAexWebhook` — pure Web Crypto,
|
|
87
|
-
identical under Bun and Node, and interoperable with the `standardwebhooks`
|
|
88
|
-
reference library:
|
|
89
|
-
|
|
90
|
-
```ts
|
|
91
|
-
import { verifyAexWebhook } from "@aexhq/sdk";
|
|
92
|
-
|
|
93
|
-
// In your HTTP handler — use the EXACT raw body bytes, never re-serialize.
|
|
94
|
-
const ok = await verifyAexWebhook({
|
|
95
|
-
rawBody,
|
|
96
|
-
headers: request.headers,
|
|
97
|
-
secret: process.env.AEX_WEBHOOK_SECRET! // the whsec_... value
|
|
98
|
-
});
|
|
99
|
-
if (!ok) return new Response("bad signature", { status: 401 });
|
|
100
|
-
```
|
|
101
|
-
|
|
102
|
-
`verifyAexWebhook` rejects stale timestamps (default tolerance 300 seconds) and
|
|
103
|
-
compares signatures constant-time. It accepts every `v1` entry in the signature
|
|
104
|
-
list, so it is already rotation-ready — but **server-side rotation of the
|
|
105
|
-
signing secret is not available yet**: `aex webhooks secret` reveals or creates
|
|
106
|
-
the one workspace secret and there is no rotate endpoint today. If your secret
|
|
107
|
-
is compromised, contact <support@aex.dev>.
|
|
108
|
-
|
|
109
|
-
## Delivery ledger and redelivery
|
|
110
|
-
|
|
111
|
-
Each run keeps a delivery ledger — attempts, last status code, and last error:
|
|
112
|
-
|
|
113
|
-
```ts
|
|
114
|
-
const deliveries = await session.webhooks().list();
|
|
115
|
-
await session.webhooks().redeliver(deliveries[0]!.id);
|
|
116
|
-
```
|
|
117
|
-
|
|
118
|
-
```bash
|
|
119
|
-
aex deliveries <session-id> --api-key "$AEX_API_KEY"
|
|
120
|
-
```
|
|
121
|
-
|
|
122
|
-
Redelivery re-sends the frozen payload with the **same** `webhook-id`, so a
|
|
123
|
-
consumer that dedupes on `webhook-id` handles retries, redeliveries, and
|
|
124
|
-
at-least-once delivery uniformly. An empty ledger means the run carried no
|
|
125
|
-
`webhook` or has not reached a terminal state yet.
|
|
126
|
-
|
|
127
|
-
For the terminal-event mechanics behind delivery timing, see
|
|
128
|
-
[Events](events.md).
|
|
12
|
+
|
|
13
|
+
```ts
|
|
14
|
+
import { Aex, Models } from "@aexhq/sdk";
|
|
15
|
+
|
|
16
|
+
const aex = new Aex(process.env.AEX_API_KEY!);
|
|
17
|
+
|
|
18
|
+
const session = await aex.openSession({
|
|
19
|
+
model: Models.CLAUDE_HAIKU_4_5,
|
|
20
|
+
webhook: { url: "https://hooks.example.com/aex" },
|
|
21
|
+
apiKeys: { anthropic: process.env.ANTHROPIC_API_KEY! }
|
|
22
|
+
});
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
aex run \
|
|
27
|
+
--api-key "$AEX_API_KEY" \
|
|
28
|
+
--anthropic-api-key "$ANTHROPIC_API_KEY" \
|
|
29
|
+
--model claude-haiku-4-5 \
|
|
30
|
+
--prompt "Write the report." \
|
|
31
|
+
--webhook https://hooks.example.com/aex
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
The URL must be `https`. The callback URL is an operational concern, not part
|
|
35
|
+
of the submission fingerprint: retrying the same submission with the same
|
|
36
|
+
idempotency key but a different callback URL never conflicts.
|
|
37
|
+
|
|
38
|
+
## What gets delivered
|
|
39
|
+
|
|
40
|
+
One POST carrying the terminal `run.finished` event, sent at the
|
|
41
|
+
settle-consistent barrier — when you receive it, the run record is already
|
|
42
|
+
terminal and its outputs are complete and readable. The body is built once at
|
|
43
|
+
settle, frozen, and re-sent byte-identical on every retry or manual redelivery:
|
|
44
|
+
|
|
45
|
+
```json
|
|
46
|
+
{
|
|
47
|
+
"specversion": "1.0",
|
|
48
|
+
"id": "<delivery id — matches the webhook-id header>",
|
|
49
|
+
"source": "aex",
|
|
50
|
+
"type": "run.finished",
|
|
51
|
+
"subject": "<the run id>",
|
|
52
|
+
"time": "2026-07-02T12:34:56.000Z",
|
|
53
|
+
"data": {
|
|
54
|
+
"runId": "<the run id>",
|
|
55
|
+
"status": "succeeded",
|
|
56
|
+
"terminalAt": "2026-07-02T12:34:56.000Z",
|
|
57
|
+
"reason": null,
|
|
58
|
+
"failureClass": null,
|
|
59
|
+
"costTelemetry": { "billedCostUsd": 0.42 }
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
`reason` / `failureClass` carry the failure detail on non-success terminals;
|
|
65
|
+
`costTelemetry` is present when the billed cost is known.
|
|
66
|
+
|
|
67
|
+
## Verify deliveries
|
|
68
|
+
|
|
69
|
+
Deliveries are signed [Standard Webhooks](https://www.standardwebhooks.com/)
|
|
70
|
+
style: HMAC-SHA256 over `` `${webhook-id}.${webhook-timestamp}.${rawBody}` ``,
|
|
71
|
+
sent in three headers — `webhook-id` (stable across retries; your dedupe key),
|
|
72
|
+
`webhook-timestamp` (unix seconds), and `webhook-signature` (a space-delimited
|
|
73
|
+
list of `v1,<base64>` entries).
|
|
74
|
+
|
|
75
|
+
The signing key is a per-workspace secret. Reveal it (it is created on first
|
|
76
|
+
use) with either surface:
|
|
77
|
+
|
|
78
|
+
```bash
|
|
79
|
+
aex webhooks secret --api-key "$AEX_API_KEY" # prints whsec_...
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
```ts
|
|
83
|
+
const { whsec } = await aex.webhookSigningSecret();
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
Verify inbound requests with the exported `verifyAexWebhook` — pure Web Crypto,
|
|
87
|
+
identical under Bun and Node, and interoperable with the `standardwebhooks`
|
|
88
|
+
reference library:
|
|
89
|
+
|
|
90
|
+
```ts
|
|
91
|
+
import { verifyAexWebhook } from "@aexhq/sdk";
|
|
92
|
+
|
|
93
|
+
// In your HTTP handler — use the EXACT raw body bytes, never re-serialize.
|
|
94
|
+
const ok = await verifyAexWebhook({
|
|
95
|
+
rawBody,
|
|
96
|
+
headers: request.headers,
|
|
97
|
+
secret: process.env.AEX_WEBHOOK_SECRET! // the whsec_... value
|
|
98
|
+
});
|
|
99
|
+
if (!ok) return new Response("bad signature", { status: 401 });
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
`verifyAexWebhook` rejects stale timestamps (default tolerance 300 seconds) and
|
|
103
|
+
compares signatures constant-time. It accepts every `v1` entry in the signature
|
|
104
|
+
list, so it is already rotation-ready — but **server-side rotation of the
|
|
105
|
+
signing secret is not available yet**: `aex webhooks secret` reveals or creates
|
|
106
|
+
the one workspace secret and there is no rotate endpoint today. If your secret
|
|
107
|
+
is compromised, contact <support@aex.dev>.
|
|
108
|
+
|
|
109
|
+
## Delivery ledger and redelivery
|
|
110
|
+
|
|
111
|
+
Each run keeps a delivery ledger — attempts, last status code, and last error:
|
|
112
|
+
|
|
113
|
+
```ts
|
|
114
|
+
const deliveries = await session.webhooks().list();
|
|
115
|
+
await session.webhooks().redeliver(deliveries[0]!.id);
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
```bash
|
|
119
|
+
aex deliveries <session-id> --api-key "$AEX_API_KEY"
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
Redelivery re-sends the frozen payload with the **same** `webhook-id`, so a
|
|
123
|
+
consumer that dedupes on `webhook-id` handles retries, redeliveries, and
|
|
124
|
+
at-least-once delivery uniformly. An empty ledger means the run carried no
|
|
125
|
+
`webhook` or has not reached a terminal state yet.
|
|
126
|
+
|
|
127
|
+
For the terminal-event mechanics behind delivery timing, see
|
|
128
|
+
[Events](events.md).
|