@mappa-ai/mappa-node 1.2.4 → 2.0.9
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 +156 -391
- package/dist/Mappa-BTrlzSaB.d.cts +621 -0
- package/dist/Mappa-BjTJceWa.d.mts +621 -0
- package/dist/index.cjs +5184 -1036
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +3 -1397
- package/dist/index.d.mts +3 -1397
- package/dist/index.mjs +5184 -1034
- package/dist/index.mjs.map +1 -1
- package/dist/node.cjs +37 -0
- package/dist/node.cjs.map +1 -0
- package/dist/node.d.cts +26 -0
- package/dist/node.d.mts +26 -0
- package/dist/node.mjs +34 -0
- package/dist/node.mjs.map +1 -0
- package/package.json +39 -28
- package/LICENSE +0 -21
package/README.md
CHANGED
|
@@ -1,474 +1,239 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @mappa-ai/mappa-node
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-

|
|
3
|
+
Official TypeScript SDK for the Mappa API.
|
|
5
4
|
|
|
6
|
-
|
|
5
|
+
Built for server and worker runtimes. Primary targets are Node.js and Bun.
|
|
7
6
|
|
|
8
|
-
|
|
9
|
-
- **Typed** end-to-end (requests, replies, errors).
|
|
10
|
-
- Built-in **retries**, **idempotency**, and **job helpers** (polling + streaming).
|
|
11
|
-
- Simple **webhook signature verification**.
|
|
7
|
+
## Agent + Dev Quickstart
|
|
12
8
|
|
|
13
|
-
|
|
9
|
+
- Install package: `npm install @mappa-ai/mappa-node`
|
|
10
|
+
- Use server-side only with `MAPPA_API_KEY`
|
|
11
|
+
- Validate package changes: `bun run check && bun run type-check && bun test`
|
|
12
|
+
- Build distribution artifacts: `bun run build`
|
|
13
|
+
- Follow package-specific agent rules: `packages/sdk-ts/AGENTS.md`
|
|
14
14
|
|
|
15
|
-
##
|
|
15
|
+
## Runtime support
|
|
16
16
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
yarn add @mappa-ai/mappa-node
|
|
21
|
-
# or
|
|
22
|
-
pnpm add @mappa-ai/mappa-node
|
|
23
|
-
# or
|
|
24
|
-
bun add @mappa-ai/mappa-node
|
|
25
|
-
```
|
|
26
|
-
|
|
27
|
-
---
|
|
28
|
-
|
|
29
|
-
## Quickstart
|
|
30
|
-
|
|
31
|
-
Create a report from a remote media URL and wait for completion:
|
|
32
|
-
|
|
33
|
-
```ts
|
|
34
|
-
import { Mappa } from "@mappa-ai/mappa-node";
|
|
17
|
+
- Primary: Node.js, Bun
|
|
18
|
+
- Compatible: Deno, Cloudflare Workers, other fetch-compatible server/edge runtimes
|
|
19
|
+
- Browser: blocked by default to protect secret API keys
|
|
35
20
|
|
|
36
|
-
|
|
37
|
-
apiKey: process.env.MAPPA_API_KEY!,
|
|
38
|
-
});
|
|
21
|
+
## Security model
|
|
39
22
|
|
|
40
|
-
|
|
41
|
-
const report = await mappa.reports.generateFromUrl({
|
|
42
|
-
url: "https://example.com/media.mp3",
|
|
43
|
-
output: { type: "markdown", template: "general_report" },
|
|
44
|
-
});
|
|
23
|
+
This SDK is designed for secret API keys.
|
|
45
24
|
|
|
25
|
+
- Do not call Mappa directly from browser apps with a private API key
|
|
26
|
+
- Put your key in a server or edge function and proxy requests
|
|
27
|
+
- The client throws in browser-like runtimes unless you set `dangerouslyAllowBrowser: true`
|
|
46
28
|
|
|
47
|
-
|
|
48
|
-
console.log(report.markdown);
|
|
49
|
-
}
|
|
50
|
-
```
|
|
29
|
+
## Server proxy pattern
|
|
51
30
|
|
|
52
|
-
|
|
31
|
+
Use your backend as a trust boundary:
|
|
53
32
|
|
|
54
|
-
|
|
33
|
+
- Browser uploads/calls your endpoint
|
|
34
|
+
- Your endpoint uses this SDK with `MAPPA_API_KEY`
|
|
35
|
+
- Your endpoint returns only safe response fields
|
|
55
36
|
|
|
56
|
-
|
|
37
|
+
### Next.js App Router example
|
|
57
38
|
|
|
58
39
|
```ts
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
```
|
|
63
|
-
|
|
64
|
-
Recommended: load the key from environment variables:
|
|
65
|
-
|
|
66
|
-
```bash
|
|
67
|
-
export MAPPA_API_KEY="your-api-key"
|
|
68
|
-
```
|
|
40
|
+
// app/api/mappa/report/route.ts
|
|
41
|
+
import { NextResponse } from "next/server";
|
|
42
|
+
import { Mappa, isMappaError } from "@mappa-ai/mappa-node";
|
|
69
43
|
|
|
70
|
-
```ts
|
|
71
44
|
const mappa = new Mappa({ apiKey: process.env.MAPPA_API_KEY! });
|
|
72
|
-
```
|
|
73
45
|
|
|
74
|
-
|
|
46
|
+
export async function POST(req: Request) {
|
|
47
|
+
try {
|
|
48
|
+
const form = await req.formData();
|
|
49
|
+
const file = form.get("file");
|
|
50
|
+
|
|
51
|
+
if (!(file instanceof File)) {
|
|
52
|
+
return NextResponse.json({ error: "file is required" }, { status: 400 });
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const report = await mappa.reports.generateFromFile({
|
|
56
|
+
file,
|
|
57
|
+
output: { template: "general_report" },
|
|
58
|
+
target: { strategy: "dominant" },
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
return NextResponse.json({
|
|
62
|
+
id: report.id,
|
|
63
|
+
template: report.output.template,
|
|
64
|
+
summary: report.summary ?? null,
|
|
65
|
+
});
|
|
66
|
+
} catch (err) {
|
|
67
|
+
if (isMappaError(err)) {
|
|
68
|
+
return NextResponse.json(
|
|
69
|
+
{ error: err.message, requestId: err.requestId ?? null },
|
|
70
|
+
{ status: 502 },
|
|
71
|
+
);
|
|
72
|
+
}
|
|
73
|
+
return NextResponse.json({ error: "Unexpected error" }, { status: 500 });
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
```
|
|
75
77
|
|
|
76
|
-
|
|
78
|
+
For advanced flow control, prefer `createJob` + `handle.wait()` or `handle.stream()`.
|
|
77
79
|
|
|
78
|
-
|
|
80
|
+
### Framework-agnostic pattern
|
|
79
81
|
|
|
80
82
|
```ts
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
baseUrl: "https://api.mappa.ai", // default
|
|
86
|
-
timeoutMs: 30_000, // default; per HTTP attempt
|
|
87
|
-
maxRetries: 2, // default
|
|
88
|
-
defaultHeaders: {
|
|
89
|
-
"X-My-App": "my-service",
|
|
90
|
-
},
|
|
91
|
-
});
|
|
83
|
+
// POST /api/report
|
|
84
|
+
// 1) Validate auth and input
|
|
85
|
+
// 2) Call Mappa on the server
|
|
86
|
+
// 3) Return sanitized output
|
|
92
87
|
```
|
|
93
88
|
|
|
94
|
-
### Request tracing
|
|
95
|
-
|
|
96
|
-
The SDK sets `X-Request-Id` on every request. You can supply your own `requestId`
|
|
97
|
-
per call to correlate logs across your system and Mappa support.
|
|
98
|
-
|
|
99
|
-
### Idempotency
|
|
100
|
-
|
|
101
|
-
Most write APIs accept `idempotencyKey`. If you do not provide one, the SDK
|
|
102
|
-
generates a best-effort key per request. For long-running workflows, it is best
|
|
103
|
-
practice to supply a stable key per logical operation.
|
|
104
|
-
|
|
105
|
-
Example:
|
|
106
|
-
|
|
107
89
|
```ts
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
90
|
+
const mappa = new Mappa({ apiKey: process.env.MAPPA_API_KEY! });
|
|
91
|
+
const report = await mappa.reports.generateFromFile({
|
|
92
|
+
file,
|
|
93
|
+
output: { template: "general_report" },
|
|
94
|
+
target: { strategy: "dominant" },
|
|
113
95
|
});
|
|
96
|
+
return { id: report.id, summary: report.summary };
|
|
114
97
|
```
|
|
115
98
|
|
|
116
|
-
###
|
|
99
|
+
### Security checklist
|
|
117
100
|
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
101
|
+
- Keep `MAPPA_API_KEY` server-only
|
|
102
|
+
- Require auth for proxy endpoints
|
|
103
|
+
- Validate input size and type before SDK calls
|
|
104
|
+
- Add rate limits on proxy endpoints
|
|
105
|
+
- Log `requestId` values from SDK errors for traceability
|
|
121
106
|
|
|
122
|
-
|
|
107
|
+
## Install
|
|
123
108
|
|
|
124
|
-
```
|
|
125
|
-
|
|
109
|
+
```bash
|
|
110
|
+
npm install @mappa-ai/mappa-node
|
|
126
111
|
```
|
|
127
112
|
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
`timeoutMs` is a **per-request** timeout (including each retry attempt).
|
|
131
|
-
For long-running work, create a job and use `jobs.wait(...)` or `reports.makeHandle(jobId)`.
|
|
132
|
-
|
|
133
|
-
### Cancelling waits
|
|
134
|
-
|
|
135
|
-
Use `AbortController` to cancel polling or streaming when your app shuts down
|
|
136
|
-
or the user navigates away.
|
|
113
|
+
## Quickstart
|
|
137
114
|
|
|
138
115
|
```ts
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
setTimeout(() => controller.abort(), 10_000);
|
|
116
|
+
import { Mappa } from "@mappa-ai/mappa-node";
|
|
142
117
|
|
|
143
|
-
const
|
|
144
|
-
|
|
145
|
-
output: { type: "markdown" },
|
|
118
|
+
const mappa = new Mappa({
|
|
119
|
+
apiKey: process.env.MAPPA_API_KEY!,
|
|
146
120
|
});
|
|
147
121
|
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
});
|
|
152
|
-
console.log(report.id);
|
|
153
|
-
} catch (err) {
|
|
154
|
-
if (err instanceof Error && err.name === "AbortError") {
|
|
155
|
-
console.log("wait canceled");
|
|
156
|
-
}
|
|
157
|
-
}
|
|
158
|
-
```
|
|
159
|
-
|
|
160
|
-
Streaming with cancellation:
|
|
161
|
-
|
|
162
|
-
```ts
|
|
163
|
-
const controller = new AbortController();
|
|
164
|
-
|
|
165
|
-
const handle = mappa.reports.makeHandle("job_...");
|
|
122
|
+
const media = await mappa.files.upload({
|
|
123
|
+
file: new Blob(["...binary..."]),
|
|
124
|
+
});
|
|
166
125
|
|
|
167
|
-
|
|
126
|
+
const receipt = await mappa.reports.createJob({
|
|
127
|
+
media: { mediaId: media.mediaId },
|
|
128
|
+
output: { template: "general_report" },
|
|
129
|
+
target: { strategy: "dominant" },
|
|
130
|
+
});
|
|
168
131
|
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
}
|
|
132
|
+
const report = await receipt.handle?.wait();
|
|
133
|
+
console.log(report?.id);
|
|
172
134
|
```
|
|
173
135
|
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
## Core concepts
|
|
177
|
-
|
|
178
|
-
### Reports are asynchronous
|
|
179
|
-
|
|
180
|
-
In the underlying architecture we do a series of transformations and
|
|
181
|
-
ML inference, which can take time.
|
|
182
|
-
To accommodate this, creating a report returns a **job receipt**. You can:
|
|
183
|
-
|
|
184
|
-
- **Wait** (poll) until completion.
|
|
185
|
-
- **Stream** job events.
|
|
186
|
-
- **Use webhooks** so your server is notified when work is done.
|
|
187
|
-
|
|
188
|
-
### Media input
|
|
136
|
+
## Node filesystem helpers
|
|
189
137
|
|
|
190
|
-
|
|
138
|
+
For Node/Bun file-path ergonomics, use the Node adapter subpath.
|
|
191
139
|
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
- Remote URLs: `reports.createJobFromUrl()` / `reports.generateFromUrl()` (download client-side → upload → create job)
|
|
197
|
-
- Local bytes: `reports.createJobFromFile()` / `reports.generateFromFile()` (upload → create job)
|
|
198
|
-
|
|
199
|
-
---
|
|
200
|
-
|
|
201
|
-
## Creating reports
|
|
202
|
-
|
|
203
|
-
### 1) Create a job (recommended for production)
|
|
140
|
+
```ts
|
|
141
|
+
import { Mappa } from "@mappa-ai/mappa-node";
|
|
142
|
+
import { uploadFromPath, generateReportFromPath } from "@mappa-ai/mappa-node/node";
|
|
204
143
|
|
|
205
|
-
|
|
144
|
+
const mappa = new Mappa({ apiKey: process.env.MAPPA_API_KEY! });
|
|
206
145
|
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
media: { mediaId: "media_..." },
|
|
210
|
-
output: { type: "markdown", template: "general_report" },
|
|
211
|
-
subject: {
|
|
212
|
-
externalRef: "customer_123",
|
|
213
|
-
metadata: { plan: "pro" },
|
|
214
|
-
},
|
|
215
|
-
options: {
|
|
216
|
-
language: "en",
|
|
217
|
-
timezone: "UTC",
|
|
218
|
-
},
|
|
219
|
-
// Optional: provide your own idempotency key for safe retries.
|
|
220
|
-
idempotencyKey: "report:customer_123:2026-01-14",
|
|
146
|
+
const media = await uploadFromPath(mappa, {
|
|
147
|
+
path: "./recording.wav",
|
|
221
148
|
});
|
|
222
149
|
|
|
223
|
-
|
|
150
|
+
const report = await generateReportFromPath(mappa, {
|
|
151
|
+
path: "./recording.wav",
|
|
152
|
+
output: { template: "general_report" },
|
|
153
|
+
target: { strategy: "dominant" },
|
|
154
|
+
});
|
|
224
155
|
```
|
|
225
156
|
|
|
226
|
-
|
|
157
|
+
## Matching analysis
|
|
227
158
|
|
|
228
159
|
```ts
|
|
229
|
-
const
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
metadata: { plan: "pro" },
|
|
235
|
-
},
|
|
236
|
-
options: {
|
|
237
|
-
language: "en",
|
|
238
|
-
timezone: "UTC",
|
|
239
|
-
},
|
|
240
|
-
idempotencyKey: "report:customer_123:2026-01-14",
|
|
160
|
+
const job = await mappa.matchingAnalysis.createJob({
|
|
161
|
+
entityA: { type: "entity_id", entityId: "entity_a" },
|
|
162
|
+
entityB: { type: "entity_id", entityId: "entity_b" },
|
|
163
|
+
context: "Compare communication style",
|
|
164
|
+
output: { template: "matching_analysis" },
|
|
241
165
|
});
|
|
242
166
|
|
|
243
|
-
|
|
167
|
+
const analysis = await job.handle?.wait();
|
|
168
|
+
console.log(analysis?.id);
|
|
244
169
|
```
|
|
245
170
|
|
|
246
|
-
|
|
171
|
+
## Observability and retries
|
|
247
172
|
|
|
248
173
|
```ts
|
|
249
|
-
const
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
174
|
+
const mappa = new Mappa({
|
|
175
|
+
apiKey: process.env.MAPPA_API_KEY!,
|
|
176
|
+
timeoutMs: 30000,
|
|
177
|
+
maxRetries: 2,
|
|
178
|
+
telemetry: {
|
|
179
|
+
onRequest: (ctx) => console.log("req", ctx.method, ctx.url, ctx.requestId),
|
|
180
|
+
onResponse: (ctx) => console.log("res", ctx.status, ctx.durationMs),
|
|
181
|
+
onError: (ctx) => console.error("err", ctx.requestId, ctx.error),
|
|
182
|
+
},
|
|
256
183
|
});
|
|
257
184
|
```
|
|
258
185
|
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
`jobs.stream(jobId)` yields state transitions.
|
|
186
|
+
## Webhooks
|
|
262
187
|
|
|
263
188
|
```ts
|
|
264
|
-
|
|
265
|
-
for (const section of report.sections) {
|
|
266
|
-
console.log(section.section_title, section.section_content);
|
|
267
|
-
}
|
|
268
|
-
}
|
|
269
|
-
```
|
|
189
|
+
const payload = rawBodyString;
|
|
270
190
|
|
|
271
|
-
|
|
191
|
+
await mappa.webhooks.verifySignature({
|
|
192
|
+
payload,
|
|
193
|
+
headers: req.headers,
|
|
194
|
+
secret: process.env.MAPPA_WEBHOOK_SECRET!,
|
|
195
|
+
});
|
|
272
196
|
|
|
273
|
-
|
|
274
|
-
if (
|
|
275
|
-
|
|
197
|
+
const event = mappa.webhooks.parseEvent(payload);
|
|
198
|
+
if (event.type === "report.completed") {
|
|
199
|
+
console.log(event.data.reportId);
|
|
276
200
|
}
|
|
277
201
|
```
|
|
278
202
|
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
## Feedback
|
|
282
|
-
|
|
283
|
-
Use `mappa.feedback.create()` to share ratings or corrections. Provide exactly
|
|
284
|
-
one of `reportId` or `jobId`.
|
|
285
|
-
|
|
286
|
-
```ts
|
|
287
|
-
await mappa.feedback.create({
|
|
288
|
-
reportId: "report_...",
|
|
289
|
-
rating: "thumbs_up",
|
|
290
|
-
tags: ["quality"],
|
|
291
|
-
comment: "Accurate summary",
|
|
292
|
-
});
|
|
293
|
-
```
|
|
294
|
-
|
|
295
|
-
---
|
|
296
|
-
|
|
297
|
-
## Errors
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
The SDK throws typed errors:
|
|
301
|
-
|
|
302
|
-
- `ApiError` for non-2xx responses
|
|
303
|
-
- `AuthError` for 401/403
|
|
304
|
-
- `ValidationError` for 422
|
|
305
|
-
- `RateLimitError` for 429 (may include `retryAfterMs`)
|
|
306
|
-
- `JobFailedError` / `JobCanceledError` from polling helpers
|
|
307
|
-
- `MappaError` for client-side validation or runtime constraints
|
|
203
|
+
## Error handling
|
|
308
204
|
|
|
309
205
|
```ts
|
|
310
206
|
import {
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
RateLimitError,
|
|
315
|
-
ValidationError,
|
|
207
|
+
isInsufficientCreditsError,
|
|
208
|
+
isMappaError,
|
|
209
|
+
isStreamError,
|
|
316
210
|
} from "@mappa-ai/mappa-node";
|
|
317
211
|
|
|
318
212
|
try {
|
|
319
|
-
|
|
320
|
-
url: "https://example.com/media.mp3",
|
|
321
|
-
output: { type: "markdown", template: "general_report" },
|
|
322
|
-
});
|
|
323
|
-
|
|
324
|
-
await mappa.reports.generateFromUrl({
|
|
325
|
-
url: "https://example.com/interview.mp3",
|
|
326
|
-
output: {
|
|
327
|
-
type: "markdown",
|
|
328
|
-
template: "hiring_report",
|
|
329
|
-
templateParams: {
|
|
330
|
-
roleTitle: "Customer Success Manager",
|
|
331
|
-
roleDescription: "Own onboarding and renewal conversations.",
|
|
332
|
-
companyCulture: "Curious, candid, customer-obsessed.",
|
|
333
|
-
},
|
|
334
|
-
},
|
|
335
|
-
});
|
|
213
|
+
// ...
|
|
336
214
|
} catch (err) {
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
throw err;
|
|
345
|
-
}
|
|
346
|
-
|
|
347
|
-
if (err instanceof ValidationError) {
|
|
348
|
-
console.error("Invalid request", err.details);
|
|
349
|
-
throw err;
|
|
350
|
-
}
|
|
351
|
-
|
|
352
|
-
if (err instanceof ApiError) {
|
|
353
|
-
console.error("API error", err.status, err.code, err.requestId);
|
|
354
|
-
throw err;
|
|
355
|
-
}
|
|
356
|
-
|
|
357
|
-
if (err instanceof MappaError) {
|
|
358
|
-
console.error("Client error", err.message);
|
|
359
|
-
throw err;
|
|
360
|
-
}
|
|
361
|
-
|
|
362
|
-
throw err;
|
|
215
|
+
if (isInsufficientCreditsError(err)) {
|
|
216
|
+
console.error(err.required, err.available);
|
|
217
|
+
} else if (isStreamError(err)) {
|
|
218
|
+
console.error(err.jobId, err.retryCount);
|
|
219
|
+
} else if (isMappaError(err)) {
|
|
220
|
+
console.error(err.requestId, err.code, err.message);
|
|
221
|
+
}
|
|
363
222
|
}
|
|
364
223
|
```
|
|
365
224
|
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
## Webhooks
|
|
369
|
-
|
|
370
|
-
Use `mappa.webhooks.verifySignature()` to verify incoming webhook events.
|
|
371
|
-
|
|
372
|
-
Tips for raw body handling:
|
|
373
|
-
- Express: `express.text({ type: "*/*" })`
|
|
374
|
-
- Fastify: use `rawBody` (enable `bodyLimit` and `rawBody`)
|
|
375
|
-
- Next.js (App Router): read `await req.text()` before parsing
|
|
376
|
-
- Node 18+ or modern runtimes are required for WebCrypto
|
|
377
|
-
|
|
378
|
-
The SDK expects a header shaped like:
|
|
379
|
-
|
|
380
|
-
```http
|
|
381
|
-
mappa-signature: t=1700000000,v1=<hex>
|
|
382
|
-
```
|
|
383
|
-
|
|
384
|
-
And verifies the HMAC-SHA256 signature of:
|
|
385
|
-
|
|
386
|
-
```ts
|
|
387
|
-
${t}.${rawBody}
|
|
388
|
-
```
|
|
389
|
-
|
|
390
|
-
Example (Express):
|
|
391
|
-
|
|
392
|
-
```ts
|
|
393
|
-
import express from "express";
|
|
394
|
-
import { Mappa } from "@mappa-ai/mappa-node";
|
|
395
|
-
|
|
396
|
-
const app = express();
|
|
397
|
-
|
|
398
|
-
// IMPORTANT: you must keep the raw body for signature verification.
|
|
399
|
-
app.post(
|
|
400
|
-
"/webhooks/mappa",
|
|
401
|
-
express.text({ type: "*/*" }),
|
|
402
|
-
async (req, res) => {
|
|
403
|
-
const mappa = new Mappa({ apiKey: process.env.MAPPA_API_KEY! });
|
|
404
|
-
|
|
405
|
-
await mappa.webhooks.verifySignature({
|
|
406
|
-
payload: req.body,
|
|
407
|
-
headers: req.headers as Record<string, string | string[] | undefined>,
|
|
408
|
-
secret: process.env.MAPPA_WEBHOOK_SECRET!,
|
|
409
|
-
toleranceSec: 300,
|
|
410
|
-
});
|
|
411
|
-
|
|
412
|
-
const event = mappa.webhooks.parseEvent(req.body);
|
|
413
|
-
|
|
414
|
-
// Example: handle event types.
|
|
415
|
-
switch (event.type) {
|
|
416
|
-
case "report.completed":
|
|
417
|
-
// event.data contains the payload from Mappa.
|
|
418
|
-
break;
|
|
419
|
-
default:
|
|
420
|
-
break;
|
|
421
|
-
}
|
|
422
|
-
|
|
423
|
-
res.status(200).send("ok");
|
|
424
|
-
},
|
|
425
|
-
);
|
|
426
|
-
```
|
|
427
|
-
|
|
428
|
-
---
|
|
225
|
+
## Scope
|
|
429
226
|
|
|
430
|
-
|
|
227
|
+
- API-key workflows only
|
|
228
|
+
- Includes: health, files, jobs, reports, matching-analysis, feedback, entities, webhooks
|
|
229
|
+
- Excludes: billing and user-session console endpoints
|
|
431
230
|
|
|
432
|
-
|
|
231
|
+
## Development
|
|
433
232
|
|
|
434
|
-
```
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
telemetry: {
|
|
440
|
-
onRequest: ({ method, url, requestId }) => {
|
|
441
|
-
console.log("request", method, url, requestId);
|
|
442
|
-
},
|
|
443
|
-
onResponse: ({ status, url, durationMs, requestId }) => {
|
|
444
|
-
console.log("response", status, url, durationMs, requestId);
|
|
445
|
-
},
|
|
446
|
-
onError: ({ url, requestId, error }) => {
|
|
447
|
-
console.log("error", url, requestId, error);
|
|
448
|
-
},
|
|
449
|
-
},
|
|
450
|
-
});
|
|
451
|
-
```
|
|
452
|
-
|
|
453
|
-
---
|
|
454
|
-
|
|
455
|
-
## TypeScript
|
|
456
|
-
|
|
457
|
-
Everything is typed. Common types are exported and `Report` is a discriminated
|
|
458
|
-
union on `report.output.type`.
|
|
459
|
-
|
|
460
|
-
```ts
|
|
461
|
-
import type {
|
|
462
|
-
Report,
|
|
463
|
-
Job,
|
|
464
|
-
ReportCreateJobRequest,
|
|
465
|
-
ReportOutput,
|
|
466
|
-
WaitOptions,
|
|
467
|
-
} from "@mappa-ai/mappa-node";
|
|
233
|
+
```bash
|
|
234
|
+
bun run build
|
|
235
|
+
bun run check
|
|
236
|
+
bun run type-check
|
|
237
|
+
bun test
|
|
468
238
|
```
|
|
469
239
|
|
|
470
|
-
---
|
|
471
|
-
|
|
472
|
-
## License
|
|
473
|
-
|
|
474
|
-
MIT
|