Skip to main content
This page covers two cross-cutting surfaces: client.asyncJobs for polling long-running jobs, and client.on(...) for subscribing to SDK lifecycle events.

Async Jobs

Endpoints that kick off long-running work — currently adverse media screening — return a jobId immediately. Most callers use the typed handle from the originating method (screening.adverseMedia(...)), but client.asyncJobs.get(jobId) lets you resume polling a jobId you persisted earlier (for example, across process restarts).

get(jobId)

get(jobId: string): Promise<AsyncJobSnapshot>
Fetch the current state of an async job by ID.
ParameterTypeRequiredDescription
jobIdstringYesServer-side job ID returned by the originating call
Returns AsyncJobSnapshot — a discriminated union on status. result is typed as unknown because the async-jobs surface is service-agnostic; re-parse it against the originating service’s schema to narrow it.
type AsyncJobSnapshot =
  | { jobId: string; createdAt: number; updatedAt: string; status: 'pending' }
  | { jobId: string; createdAt: number; updatedAt: string; status: 'processing' }
  | { jobId: string; createdAt: number; updatedAt: string; status: 'ready'; result: unknown }
  | { jobId: string; createdAt: number; updatedAt: string; status: 'failed'; error: string };
createdAt is epoch seconds (a number); updatedAt is an ISO 8601 string. ready and failed are the terminal states.
Throws ValidationError (empty jobId), AuthenticationError (401), AuthorizationError (403), NotFoundError (404 — unknown ID or pruned by TTL), DeepIDVError.
const snapshot = await client.asyncJobs.get('job_abc123');

if (snapshot.status === 'ready') {
  console.log(snapshot.result); // unknown — re-parse with the service's schema
} else if (snapshot.status === 'failed') {
  console.error(snapshot.error);
} else {
  console.log(`Still ${snapshot.status}…`);
}
See also: REST Get Async Job.

Events

The SDK emits lifecycle events for observability, logging, and APM integration. Subscribe with client.on(event, listener).

on(event, listener)

on<K extends keyof SDKEventMap>(
  event: K,
  listener: (payload: SDKEventMap[K]) => void,
): () => void
Subscribe to an event. Returns an unsubscribe function — call it to remove the listener.
const unsubscribe = client.on('request', ({ method, url }) => {
  console.log(`→ ${method} ${url}`);
});

// Later
unsubscribe();
For one-shot behavior, unsubscribe from inside the listener:
const unsub = client.on('response', (payload) => {
  console.log('First response:', payload.status);
  unsub();
});

Event map

type SDKEventMap = {
  request: { method: string; url: string };
  response: { status: number; url: string; durationMs: number };
  retry: { attempt: number; delayMs: number; error: unknown };
  error: { error: unknown };
  warning: { message: string; error: unknown };
  'upload:start': { url: string; bytes: number; contentType: string };
  'upload:complete': { url: string; contentType: string };
};
EventFiredPayload
requestBefore each HTTP request{ method, url }
responseAfter each successful response{ status, url, durationMs }
retryBefore each retry sleep{ attempt, delayMs, error }
errorWhen all retries are exhausted, before throwing{ error }
warningWhen a listener itself throws{ message, error }
upload:startBefore each S3 PUT upload{ url, bytes, contentType }
upload:completeAfter each S3 PUT upload{ url, contentType }

Execution model

Events dispatch synchronously within the request flow, in registration order. The SDK does not await listener return values. Therefore:
  • Listeners should not perform heavy blocking work — use async logging instead.
  • Listeners cannot modify the request or response — payloads are read-only.

Listener error safety

If a listener throws, the SDK catches the exception, emits a warning event with the details, and continues processing normally — a broken listener never crashes a request. If a warning listener itself throws, that exception is silently swallowed to prevent infinite recursion.
// This broken listener won't crash your application:
client.on('request', () => {
  throw new Error('oops');
});
// The SDK catches it and emits:
// warning: { message: "Listener error in 'request'", error: Error('oops') }

APM integration example

import { DeepIDV } from '@deepidv/server';

const client = new DeepIDV({ apiKey: process.env.DEEPIDV_API_KEY! });

client.on('request', ({ method, url }) => {
  tracer.trace('deepidv.request', { resource: `${method} ${url}` });
});

client.on('response', ({ status, durationMs }) => {
  metrics.histogram('deepidv.latency', durationMs);
  metrics.increment('deepidv.requests', { status: String(status) });
});

client.on('retry', ({ attempt }) => {
  metrics.increment('deepidv.retries', { attempt: String(attempt) });
});

client.on('error', ({ error }) => {
  errorTracker.captureException(error);
});