Jobs resource
The mappa.jobs resource provides methods for monitoring job status, streaming real-time progress, and canceling running jobs.
Overview​
Report generation happens asynchronously through jobs. The Jobs resource handles:
- Fetching job status
- Polling until completion
- Streaming real-time progress events
- Canceling running jobs
Methods​
get()​
Get the current status of a job.
const job = await mappa.jobs.get("job_abc123");
console.info(`Status: ${job.status}`);
console.info(`Stage: ${job.stage}`);
console.info(`Progress: ${Math.round(job.progress! * 100)}%`);
Parameters​
get(
jobId: string,
opts?: {
requestId?: string;
signal?: AbortSignal;
}
): Promise<Job>
Returns​
{
id: string;
type: "report.generate";
status: "queued" | "running" | "succeeded" | "failed" | "canceled";
stage?: "uploaded" | "queued" | "transcoding" | "extracting" | "scoring" | "rendering" | "finalizing";
progress?: number; // 0..1
createdAt: string;
updatedAt: string;
reportId?: string; // Available when status === "succeeded"
usage?: {
creditsUsed: number;
creditsDiscounted?: number;
creditsNetUsed: number;
durationMs?: number;
modelVersion?: string;
};
credits?: {
reservedCredits: number | null;
reservationStatus: "active" | "released" | "applied" | null;
};
error?: {
code: string;
message: string;
details?: JsonValue;
retryable?: boolean;
};
requestId?: string;
}
Example​
const job = await mappa.jobs.get("job_abc123");
if (job.status === "succeeded" && job.reportId) {
const report = await mappa.reports.get(job.reportId);
console.info(report);
} else if (job.status === "failed") {
console.error(`Job failed: ${job.error?.message}`);
}
wait()​
Poll a job until it reaches a terminal state (succeeded, failed, or canceled).
const job = await mappa.jobs.wait("job_abc123", {
timeoutMs: 5 * 60_000, // 5 minutes
onEvent: (event) => {
if (event.type === "stage") {
console.info(`Stage: ${event.stage}`);
}
},
});
console.info(`Final status: ${job.status}`);
Parameters​
wait(
jobId: string,
opts?: {
timeoutMs?: number; // Default: 300000 (5 min)
onEvent?: (event: JobEvent) => void;
signal?: AbortSignal;
}
): Promise<Job>
Wait options​
| Option | Type | Default | Description |
|---|---|---|---|
timeoutMs | number | 300000 | Max time to wait (5 minutes) |
onEvent | function | - | Callback for job events |
signal | AbortSignal | - | Abort signal for cancellation |
Returns​
Returns the final Job object when the job reaches a terminal state.
Throws:
JobFailedError- if job status becomes"failed"JobCanceledError- if job status becomes"canceled"JobFailedError(timeout) - iftimeoutMsis exceeded
Examples​
Basic wait:
import { JobFailedError } from "@mappa-ai/mappa-node";
try {
const job = await mappa.jobs.wait("job_abc123");
console.info(`Job completed: ${job.reportId}`);
} catch (err) {
if (err instanceof JobFailedError) {
console.error("Job failed:", err.message);
}
}
Wait with progress:
const job = await mappa.jobs.wait("job_abc123", {
timeoutMs: 10 * 60_000,
onEvent: (event) => {
if (event.type === "stage") {
const percent = Math.round(event.progress! * 100);
console.info(`${event.stage}: ${percent}%`);
}
},
});
Wait with cancellation:
const controller = new AbortController();
// Cancel after 2 minutes
setTimeout(() => controller.abort(), 2 * 60_000);
try {
const job = await mappa.jobs.wait("job_abc123", {
signal: controller.signal,
});
} catch (err) {
if (err instanceof Error && err.name === "AbortError") {
console.info("Wait canceled");
}
}
stream()​
Stream job events as an async iterator. Yields events when job status or stage changes.
for await (const event of mappa.jobs.stream("job_abc123")) {
if (event.type === "stage") {
console.info(`${event.stage}: ${event.progress}`);
}
if (event.type === "terminal") break;
}
Parameters​
stream(
jobId: string,
opts?: {
signal?: AbortSignal;
onEvent?: (event: JobEvent) => void;
}
): AsyncIterable<JobEvent>
Event types​
type JobEvent =
| { type: "status"; job: Job }
| { type: "stage"; stage: JobStage; progress?: number; job: Job }
| { type: "log"; message: string; ts: string }
| { type: "terminal"; job: Job };
| Event Type | Description | Fields |
|---|---|---|
status | Job status changed | job |
stage | Processing stage changed | stage, progress, job |
log | Log message (future) | message, ts |
terminal | Job reached terminal state | job |
Examples​
Basic streaming:
for await (const event of mappa.jobs.stream("job_abc123")) {
switch (event.type) {
case "status":
console.info(`Status: ${event.job.status}`);
break;
case "stage":
const percent = Math.round(event.progress! * 100);
console.info(`${event.stage}: ${percent}%`);
break;
case "terminal":
console.info("Job complete!");
break;
}
if (event.type === "terminal") break;
}
Stream with progress bar:
import cliProgress from "cli-progress";
const bar = new cliProgress.SingleBar({});
bar.start(100, 0);
for await (const event of mappa.jobs.stream("job_abc123")) {
if (event.type === "stage" && event.progress) {
bar.update(Math.round(event.progress * 100));
}
if (event.type === "terminal") break;
}
bar.stop();
Stream with cancellation:
const controller = new AbortController();
setTimeout(() => controller.abort(), 60_000);
try {
for await (const event of mappa.jobs.stream("job_abc123", {
signal: controller.signal,
})) {
console.info(event);
if (event.type === "terminal") break;
}
} catch (err) {
if (err instanceof Error && err.name === "AbortError") {
console.info("Stream canceled");
}
}
cancel()​
Cancel a running job. Jobs in queued or running status can be canceled.
const job = await mappa.jobs.cancel("job_abc123");
console.info(`Status: ${job.status}`); // "canceled"
Parameters​
cancel(
jobId: string,
opts?: {
idempotencyKey?: string;
requestId?: string;
signal?: AbortSignal;
}
): Promise<Job>
Returns​
Returns the updated Job object with status: "canceled".
Example​
const job = await mappa.jobs.cancel("job_abc123", {
idempotencyKey: "cancel:job_abc123",
});
if (job.status === "canceled") {
console.info("Job successfully canceled");
}
Canceling a job is idempotent. Canceling an already-canceled job returns the same result.
Job lifecycle​
graph LR
A[queued] --> B[running]
B --> C{Outcome?}
C -->|Success| D[succeeded]
C -->|Error| E[failed]
C -->|Canceled| F[canceled]
A -.->|cancel()| F
B -.->|cancel()| F
Job statuses​
| Status | Description | Terminal? |
|---|---|---|
queued | Waiting to start | No |
running | Currently processing | No |
succeeded | Completed successfully | Yes |
failed | Processing failed | Yes |
canceled | Manually canceled | Yes |
Job stages​
Jobs in running status progress through these stages:
| Stage | Description |
|---|---|
uploaded | Media uploaded |
queued | Queued for processing |
transcoding | Converting media format |
extracting | Extracting audio features |
scoring | Running behavioral analysis |
rendering | Generating report output |
finalizing | Final cleanup |
Common patterns​
Poll until complete​
import { JobFailedError, JobCanceledError } from "@mappa-ai/mappa-node";
try {
const job = await mappa.jobs.wait("job_abc123", {
timeoutMs: 10 * 60_000,
onEvent: (event) => {
if (event.type === "stage") {
console.info(`Processing: ${event.stage}`);
}
},
});
// Job succeeded, fetch report
const report = await mappa.reports.get(job.reportId!);
console.info(report);
} catch (err) {
if (err instanceof JobFailedError) {
console.error(`Job failed: ${err.message}`);
} else if (err instanceof JobCanceledError) {
console.error("Job was canceled");
} else {
throw err;
}
}
Monitor multiple jobs​
const jobIds = ["job_1", "job_2", "job_3"];
// Wait for all jobs concurrently
const results = await Promise.allSettled(
jobIds.map((id) => mappa.jobs.wait(id))
);
for (const [i, result] of results.entries()) {
if (result.status === "fulfilled") {
console.info(`Job ${jobIds[i]}: succeeded`);
} else {
console.error(`Job ${jobIds[i]}: failed`, result.reason);
}
}
Stream with Real-time updates​
const jobId = "job_abc123";
for await (const event of mappa.jobs.stream(jobId)) {
switch (event.type) {
case "status":
await updateDB({ jobId, status: event.job.status });
break;
case "stage":
await updateDB({
jobId,
stage: event.stage,
progress: event.progress,
});
break;
case "terminal":
if (event.job.status === "succeeded") {
await processReport(event.job.reportId!);
}
break;
}
if (event.type === "terminal") break;
}
Timeout and retry​
async function waitWithRetry(jobId: string, maxRetries = 3) {
for (let attempt = 1; attempt <= maxRetries; attempt++) {
try {
const job = await mappa.jobs.wait(jobId, {
timeoutMs: 5 * 60_000, // 5 min per attempt
});
return job;
} catch (err) {
if (err instanceof JobFailedError) {
// Job failed permanently, don't retry
throw err;
}
if (attempt < maxRetries) {
console.warn(`Timeout on attempt ${attempt}, retrying...`);
continue;
}
throw err;
}
}
}
Graceful shutdown​
const controller = new AbortController();
// Cancel on SIGTERM
process.on("SIGTERM", () => {
console.info("Shutting down, canceling job wait...");
controller.abort();
});
try {
const job = await mappa.jobs.wait("job_abc123", {
signal: controller.signal,
});
console.info("Job completed");
} catch (err) {
if (err instanceof Error && err.name === "AbortError") {
console.info("Wait canceled due to shutdown");
// Optionally cancel the job itself
await mappa.jobs.cancel("job_abc123");
}
}
Error handling​
import {
JobFailedError,
JobCanceledError,
ApiError,
} from "@mappa-ai/mappa-node";
try {
const job = await mappa.jobs.wait("job_abc123");
} catch (err) {
if (err instanceof JobFailedError) {
console.error(`Job ${err.jobId} failed:`, err.message);
console.error("Error code:", err.code);
// Check if retryable
const jobStatus = await mappa.jobs.get(err.jobId);
if (jobStatus.error?.retryable) {
console.info("Error is retryable, can retry job");
}
} else if (err instanceof JobCanceledError) {
console.error(`Job ${err.jobId} was canceled`);
} else if (err instanceof ApiError) {
console.error("API error:", err.message);
}
}
Job events reference​
Status event​
Emitted when job status changes:
{
type: "status";
job: Job;
}
Stage event​
Emitted when processing stage changes:
{
type: "stage";
stage: "uploaded" | "queued" | "transcoding" | "extracting" | "scoring" | "rendering" | "finalizing";
progress?: number; // 0..1
job: Job;
}
Terminal event​
Emitted when job reaches a terminal state:
{
type: "terminal";
job: Job; // job.status is "succeeded", "failed", or "canceled"
}
Log event (future)​
Reserved for future streaming logs:
{
type: "log";
message: string;
ts: string;
}
Type reference​
import type {
Job,
JobStatus,
JobStage,
JobEvent,
WaitOptions,
} from "@mappa-ai/mappa-node";
import {
JobFailedError,
JobCanceledError,
} from "@mappa-ai/mappa-node";
See TypeScript Types for complete type definitions.
Next steps​
- Reports Resource - Create and retrieve reports
- Webhooks - Get notified when jobs complete
- Error Handling - Handle errors gracefully
- Production Guide - Best practices for production