Skip to main content

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;
}
ParameterTypeRequiredDescription
fileBlob | ArrayBuffer | Uint8Array | ReadableStreamYesFile data to upload
labelstringNoHuman-readable label for the uploaded media
idempotencyKeystringNoUnique key for safe retries
requestIdstringNoRequest correlation ID
signalAbortSignalNoAbortController 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​

ExtensionContent Type
.mp3audio/mpeg
.wavaudio/wav
.m4aaudio/mp4
.mp4video/mp4
.movvideo/quicktime
.webmvideo/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​