Production guide π
Best practices for running Mappa SDK in production. Follow these patterns to build reliable, scalable integrations.
Architecture patternsβ
Use webhooks, not pollingβ
β Don't:
// Blocks for minutes, wastes resources
const report = await mappa.reports.generateFromUrl({
url: "https://example.com/call.mp3",
output: { template: "general_report" },
target: { strategy: "dominant" },
});
β Do:
// Returns immediately, scales infinitely
const receipt = await mappa.reports.createJobFromUrl({
url: "https://example.com/call.mp3",
output: { template: "sales_playbook" },
webhook: { url: "https://myapp.com/webhooks/mappa" },
idempotencyKey: `report:${userId}:${mediaHash}`,
target: { strategy: "dominant" },
});
await db.jobs.create({ jobId: receipt.jobId, userId, status: receipt.status });
Idempotencyβ
Prevent duplicate jobsβ
Always use idempotency keys:
await mappa.reports.createJobFromUrl({
url: "https://example.com/call.mp3",
output: { template: "sales_playbook" },
// Stable key prevents duplicates on retry
idempotencyKey: `report:user_${userId}:${mediaHash}`,
target: { strategy: "dominant" },
});
Key strategies:
| Strategy | Example | Use Case |
|---|---|---|
| User + Media | report:user_123:media_abc | One report per user per file |
| External ID | report:customer_456:order_789 | Link to your system |
| UUID | report:${uuidv4()} | Unique every time |
Handle duplicate webhooksβ
Webhooks may be delivered multiple times:
async function processWebhook(event: WebhookEvent) {
// Check if already processed
const existing = await db.webhookEvents.findOne({ id: event.id });
if (existing) {
return; // Idempotent: safe to skip
}
// Process event
await handleEvent(event);
// Mark as processed
await db.webhookEvents.create({
id: event.id,
type: event.type,
processedAt: new Date(),
});
}
Learn more about idempotency β
Request tracingβ
Use correlation IDsβ
Track requests across your system:
const requestId = `req_user_${userId}_${Date.now()}`;
const receipt = await mappa.reports.createJobFromUrl({
url: "https://example.com/call.mp3",
output: { template: "sales_playbook" },
requestId,
target: { strategy: "dominant" },
});
logger.info({ requestId, jobId: receipt.jobId }, "Job created");
Benefits:
- Trace requests end-to-end
- Correlate logs with Mappa support
- Debug faster
Monitor with telemetryβ
const mappa = new Mappa({
apiKey: process.env.MAPPA_API_KEY!,
telemetry: {
onRequest: ({ method, url, requestId }) => {
metrics.increment("mappa.request", { method });
logger.debug({ method, url, requestId }, "Request");
},
onResponse: ({ status, durationMs, requestId }) => {
metrics.timing("mappa.latency", durationMs);
metrics.increment("mappa.response", { status });
},
onError: ({ error, requestId }) => {
metrics.increment("mappa.error");
logger.error({ error, requestId }, "Error");
},
},
});
Error handlingβ
Retry with exponential backoffβ
The SDK retries automatically, but you may need custom logic:
async function createJobWithRetry(params: any, maxRetries = 3) {
for (let i = 0; i < maxRetries; i++) {
try {
return await mappa.reports.createJob(params);
} catch (err) {
if (i === maxRetries - 1) throw err;
if (err instanceof RateLimitError) {
await sleep(Math.pow(2, i) * 1000);
} else {
throw err; // Don't retry non-retryable errors
}
}
}
}
Graceful degradationβ
Handle failures without breaking the user experience:
async function generateReportWithFallback(url: string) {
try {
return await mappa.reports.generateFromUrl({
url,
output: { template: "sales_playbook" },
target: { strategy: "dominant" },
});
} catch (err) {
if (err instanceof InsufficientCreditsError) {
// Notify admin, use cached report
await notifyLowCredits();
return getCachedReport(url);
}
if (err instanceof ValidationError) {
// Fallback to general template
return await mappa.reports.generateFromUrl({
url,
output: { template: "general_report" },
target: { strategy: "dominant" },
});
}
throw err;
}
}
Timeout configurationβ
Adjust for long filesβ
// Default client
const mappa = new Mappa({
apiKey: process.env.MAPPA_API_KEY!,
timeoutMs: 30000, // 30 seconds
});
// Override for specific request
await mappa.reports.createJobFromUrl({
url: "https://example.com/long-video.mp4",
output: { template: "general_report" },
}, {
timeoutMs: 120000, // 2 minutes for this request
target: { strategy: "dominant" },
});
Recommended timeoutsβ
| Operation | Timeout |
|---|---|
| File upload | 60-120 seconds |
| Job creation | 30 seconds |
| Status check | 10 seconds |
| Webhook delivery | 5 seconds |
Credit managementβ
Check balance before jobsβ
async function canCreateJob(estimatedCredits: number): Promise<boolean> {
const { available } = await mappa.credits.getBalance();
return available >= estimatedCredits;
}
// Use it
if (await canCreateJob(50)) {
await mappa.reports.createJob({
media: { mediaId: "media_123" },
output: { template: "general_report" },
target: { strategy: "dominant" },
});
} else {
await notifyLowCredits();
}
Track usageβ
async function trackCreditUsage(jobId: string) {
const usage = await mappa.credits.getJobUsage(jobId);
await analytics.track("credit_usage", {
jobId,
creditsUsed: usage.creditsCharged,
creditsReserved: usage.creditsReserved,
creditsRefunded: usage.creditsRefunded,
});
}
Securityβ
Environment variablesβ
.env
MAPPA_API_KEY=your_production_key_here
MAPPA_WEBHOOK_SECRET=your_webhook_secret_here
const mappa = new Mappa({
apiKey: process.env.MAPPA_API_KEY!,
});
Never Expose Keys
- β Don't commit keys to version control
- β Don't use keys in client-side code
- β Don't log keys in error messages
- β Use separate keys for dev/staging/prod
Webhook verificationβ
Always verify webhook signatures:
await mappa.webhooks.verifySignature({
payload: req.body,
headers: req.headers,
secret: process.env.MAPPA_WEBHOOK_SECRET!,
});
Performanceβ
Reuse client instancesβ
// β
Good: One client for entire app
const mappa = new Mappa({ apiKey: process.env.MAPPA_API_KEY! });
// β Bad: Creating new client every request
app.post("/analyze", async (req, res) => {
const mappa = new Mappa({ apiKey: process.env.MAPPA_API_KEY! }); // Don't do this!
});
Batch operationsβ
// Process multiple files concurrently
async function batchAnalyze(urls: string[]) {
const jobs = await Promise.all(
urls.map(url =>
mappa.reports.createJobFromUrl({
url,
output: { template: "general_report" },
target: { strategy: "dominant" },
webhook: { url: "https://myapp.com/webhooks/mappa" },
})
)
);
return jobs.map(j => j.jobId);
}
Monitoringβ
Key metrics to trackβ
// Request volume
metrics.increment("mappa.jobs.created");
// Latency
metrics.timing("mappa.job.duration", durationMs);
// Success rate
metrics.increment("mappa.jobs.succeeded");
metrics.increment("mappa.jobs.failed");
// Credit usage
metrics.gauge("mappa.credits.available", available);
Health checksβ
async function checkMappaHealth() {
try {
await mappa.health.ping({ timeoutMs: 5000 });
return { status: "healthy" };
} catch (err) {
return { status: "unhealthy", error: err.message };
}
}
// Health endpoint
app.get("/health", async (req, res) => {
const mappa = await checkMappaHealth();
res.json({ services: { mappa } });
});
Testingβ
Mock the clientβ
// __mocks__/@mappa-ai/mappa-node.ts
export const Mappa = jest.fn().mockImplementation(() => ({
reports: {
createJobFromUrl: jest.fn().mockResolvedValue({
jobId: "job_test123",
status: "queued",
}),
get: jest.fn().mockResolvedValue({
id: "report_test123",
markdown: "# Test Report",
}),
},
}));
Integration testsβ
describe("Report generation", () => {
it("creates job and handles webhook", async () => {
const receipt = await mappa.reports.createJobFromUrl({
url: "https://example.com/test.mp3",
output: { template: "general_report" },
target: { strategy: "dominant" },
});
expect(receipt.jobId).toMatch(/^job_/);
expect(receipt.status).toBe("queued");
});
});
Deployment checklistβ
Before going live:
Configurationβ
- Set
MAPPA_API_KEYin production environment - Set
MAPPA_WEBHOOK_SECRETfor webhook verification - Configure appropriate
timeoutMsvalues - Set up separate keys for staging and production
Monitoringβ
- Set up telemetry hooks
- Track key metrics (jobs created, success rate, credits)
- Configure alerts for errors and low credits
- Add health checks
Error handlingβ
- Implement retry logic with exponential backoff
- Add circuit breakers for cascading failures
- Set up error logging and alerting
- Test graceful degradation
Webhooksβ
- Set up webhook endpoint
- Verify signature on all webhooks
- Handle idempotent webhook delivery
- Test with ngrok or webhook.site
Testingβ
- Write unit tests with mocked client
- Run integration tests against sandbox
- Load test with expected production volume
- Test failure scenarios
What's next? π―β
Deep dives:
- Error Handling - Handle all error types
- Idempotency - Prevent duplicate operations
- Webhooks - Real-time notifications
Resources:
- Examples - Production-ready code samples
- API Reference - Complete API docs