Files resource
The mappa.files resource provides methods for uploading, listing, and managing media files.
Overview​
Media files must be uploaded before creating report jobs. The Files resource handles:
- Multipart file uploads
- File metadata retrieval
- Paginated file listing
- Retention lock management
- File deletion
Methods​
upload()​
Upload a media file to Mappa.
const upload = await mappa.files.upload({
file: fileBuffer,
label: "recording.mp3",
});
console.info(`Uploaded: ${upload.mediaId}`);
Parameters​
{
file: Blob | ArrayBuffer | Uint8Array | ReadableStream<Uint8Array>;
label?: string;
idempotencyKey?: string;
requestId?: string;
signal?: AbortSignal;
}
| Parameter | Type | Required | Description |
|---|---|---|---|
file | Blob | ArrayBuffer | Uint8Array | ReadableStream | Yes | File data to upload |
label | string | No | Human-readable label for the uploaded media |
idempotencyKey | string | No | Unique key for safe retries |
requestId | string | No | Request correlation ID |
signal | AbortSignal | No | AbortController signal for cancellation |
Returns​
{
mediaId: string;
createdAt: string;
contentType: string;
label?: string | null;
sizeBytes?: number;
durationSeconds?: number;
}
Examples​
Upload from buffer:
import { readFile } from "node:fs/promises";
const buffer = await readFile("./recording.mp3");
const upload = await mappa.files.upload({
file: buffer,
label: "recording.mp3",
});
Upload with idempotency:
const upload = await mappa.files.upload({
file: fileBuffer,
label: "recording.mp3",
idempotencyKey: "upload:customer_123:recording_1",
});
Upload with cancellation:
const controller = new AbortController();
setTimeout(() => controller.abort(), 30_000); // Cancel after 30s
try {
const upload = await mappa.files.upload({
file: largeFileBuffer,
label: "large-recording.mp4",
signal: controller.signal,
});
} catch (err) {
if (err instanceof Error && err.name === "AbortError") {
console.info("Upload canceled");
}
}
get()​
Retrieve metadata for a single file.
const file = await mappa.files.get("media_abc123xyz");
console.info(`Status: ${file.processingStatus}`);
Parameters​
get(mediaId: string, opts?: {
requestId?: string;
signal?: AbortSignal;
}): Promise<MediaFile>
Returns​
{
mediaId: string;
createdAt: string;
contentType: string;
label?: string | null;
sizeBytes: number | null;
durationSeconds: number | null;
processingStatus: "PENDING" | "PROCESSING" | "COMPLETED" | "FAILED";
lastUsedAt: string | null;
retention: {
expiresAt: string | null;
daysRemaining: number | null;
locked: boolean;
};
}
Example​
const file = await mappa.files.get("media_abc123xyz");
if (file.processingStatus === "COMPLETED") {
console.info(`Duration: ${file.durationSeconds}s`);
console.info(`Expires: ${file.retention.expiresAt}`);
}
list()​
List uploaded files with cursor-based pagination.
const page = await mappa.files.list({ limit: 20 });
for (const file of page.files) {
console.info(`${file.mediaId}: ${file.sizeBytes} bytes`);
}
if (page.hasMore) {
const nextPage = await mappa.files.list({
limit: 20,
cursor: page.nextCursor,
});
}
Parameters​
{
limit?: number; // Max files per page (1-100, default 20)
cursor?: string; // Pagination cursor from previous response
includeDeleted?: boolean; // Include soft-deleted files (default false)
requestId?: string;
signal?: AbortSignal;
}
Returns​
{
files: MediaFile[];
nextCursor: string | null;
hasMore: boolean;
}
Example​
// First page
const page1 = await mappa.files.list({ limit: 50 });
console.info(`Found ${page1.files.length} files`);
// Next page (if available)
if (page1.hasMore) {
const page2 = await mappa.files.list({
limit: 50,
cursor: page1.nextCursor,
});
}
listAll()​
Async iterator that automatically handles pagination.
for await (const file of mappa.files.listAll()) {
console.info(`${file.mediaId}: ${file.processingStatus}`);
}
Parameters​
{
limit?: number; // Files per page (default 20)
includeDeleted?: boolean; // Include deleted files (default false)
requestId?: string;
signal?: AbortSignal;
}
Examples​
Iterate all files:
for await (const file of mappa.files.listAll({ limit: 100 })) {
console.info(`Media ID: ${file.mediaId}`);
console.info(`Size: ${file.sizeBytes} bytes`);
}
Collect all files:
const allFiles: MediaFile[] = [];
for await (const file of mappa.files.listAll()) {
allFiles.push(file);
}
console.info(`Total files: ${allFiles.length}`);
With cancellation:
const controller = new AbortController();
try {
for await (const file of mappa.files.listAll({ signal: controller.signal })) {
if (allFiles.length >= 1000) {
controller.abort(); // Stop after 1000 files
}
}
} catch (err) {
if (err instanceof Error && err.name === "AbortError") {
console.info("Iteration canceled");
}
}
setRetentionLock()​
Lock or unlock a file's retention to prevent or allow automatic deletion.
// Prevent automatic deletion
await mappa.files.setRetentionLock("media_abc123", true);
// Allow automatic deletion
await mappa.files.setRetentionLock("media_abc123", false);
Parameters​
setRetentionLock(
mediaId: string,
locked: boolean,
opts?: {
requestId?: string;
signal?: AbortSignal;
}
): Promise<RetentionLockResult>
Returns​
{
mediaId: string;
retentionLock: boolean;
message: string;
}
Example​
const result = await mappa.files.setRetentionLock("media_abc123", true);
console.info(result.message); // "Retention lock enabled"
delete()​
Soft-delete a file. Deleted files are retained for a grace period before permanent deletion.
const result = await mappa.files.delete("media_abc123");
console.info(`Deleted: ${result.deleted}`); // true
Parameters​
delete(
mediaId: string,
opts?: {
idempotencyKey?: string;
requestId?: string;
signal?: AbortSignal;
}
): Promise<FileDeleteReceipt>
Returns​
{
mediaId: string;
deleted: true;
}
Example​
try {
await mappa.files.delete("media_abc123", {
idempotencyKey: "delete:media_abc123",
});
console.info("File deleted");
} catch (err) {
if (err instanceof ApiError) {
console.error("Delete failed:", err.message);
}
}
Common patterns​
Upload and create report​
// 1. Upload file
const upload = await mappa.files.upload({
file: fileBuffer,
label: "call.mp3",
});
// 2. Create report job
const receipt = await mappa.reports.createJob({
media: { mediaId: upload.mediaId },
output: { template: "sales_playbook" },
target: { strategy: "dominant" },
});
// 3. Wait for completion
const report = await receipt.handle!.wait();
Check processing status​
const file = await mappa.files.get("media_abc123");
switch (file.processingStatus) {
case "PENDING":
console.info("File queued for processing");
break;
case "PROCESSING":
console.info("File is being processed");
break;
case "COMPLETED":
console.info("File ready for analysis");
break;
case "FAILED":
console.error("File processing failed");
break;
}
List files by status​
const completedFiles: MediaFile[] = [];
for await (const file of mappa.files.listAll()) {
if (file.processingStatus === "COMPLETED") {
completedFiles.push(file);
}
}
console.info(`${completedFiles.length} files ready`);
Batch upload​
import { readdir, readFile } from "node:fs/promises";
const files = await readdir("./recordings");
const uploads = await Promise.all(
files.map(async (filename) => {
const buffer = await readFile(`./recordings/${filename}`);
return mappa.files.upload({
file: buffer,
label: filename,
idempotencyKey: `upload:batch:${filename}`,
});
})
);
console.info(`Uploaded ${uploads.length} files`);
Retention management​
// Lock important files
for await (const file of mappa.files.listAll()) {
if (file.lastUsedAt && file.retention.daysRemaining && file.retention.daysRemaining < 7) {
await mappa.files.setRetentionLock(file.mediaId, true);
console.info(`Locked ${file.mediaId} (expires soon)`);
}
}
Content type detection​
The SDK detects content type from the uploaded Blob when available.
For ArrayBuffer/Uint8Array/ReadableStream inputs, the service determines content type server-side.
Supported extensions​
| Extension | Content Type |
|---|---|
.mp3 | audio/mpeg |
.wav | audio/wav |
.m4a | audio/mp4 |
.mp4 | video/mp4 |
.mov | video/quicktime |
.webm | video/webm |
For uncommon formats, pass a Blob with an explicit MIME type:
await mappa.files.upload({
file: new Blob([buffer], { type: "audio/ogg" }),
label: "recording.ogg",
});
Error handling​
import { ApiError, ValidationError, RateLimitError } from "@mappa-ai/mappa-node";
try {
const upload = await mappa.files.upload({
file: buffer,
label: "recording.mp3",
});
} catch (err) {
if (err instanceof ValidationError) {
console.error("Invalid file:", err.details);
} else if (err instanceof RateLimitError) {
console.error("Rate limited, retry after:", err.retryAfterMs);
} else if (err instanceof ApiError) {
console.error("Upload failed:", err.message);
}
}
Type reference​
import type {
MediaObject,
MediaFile,
ListFilesResponse,
RetentionLockResult,
FileDeleteReceipt,
} from "@mappa-ai/mappa-node";
See TypeScript Types for complete type definitions.
Next steps​
- Reports Resource - Generate reports from uploaded files
- Usage Guide - Learn core SDK patterns
- Error Handling - Handle errors gracefully