> Reverse Contact API documentation. This is the Markdown copy of https://docs.reversecontact.com/sdk/javascript/async-jobs
> Every page is listed at https://docs.reversecontact.com/llms.txt, every endpoint with its credit cost and status at https://docs.reversecontact.com/catalog.json

# Async jobs

> Wait for live profiles, Find Email, Verify Email and Find Phone with the TypeScript SDK, resume saved job ids, and parse webhook deliveries.

- Last verified: 2026-11-04

Live fetches and contact lookups return a `Job` before the result is ready.

## Start and wait for a job

`fetch.personLive`, `fetch.companyLive`, `contact.findEmail`, `contact.verifyEmail` and `contact.findPhone` start a job and return a `Job` right away. `job.id` is the job's `webhookId`.

Call `job.wait()` to poll until the job ends. When the job succeeds, the method returns its final state (`data.status`, `data.result`, `data.creditsConsumed`). When the job fails, the method raises `JobFailedError`. Polling is free and does not count toward your rate limit.

**Live fetch**

```typescript [TypeScript SDK]
import ReverseContact, { JobFailedError, JobTimeoutError } from "@reverse-contact/sdk";

const client = new ReverseContact({ apiKey: process.env.RC_API_KEY });

const job = await client.fetch.personLive({ url: "https://social.com/in/janedoe" });
// Save the id first: after a restart, client.jobs.wait(id) resumes without paying for a new job.
console.log("Job id:", job.id);

try {
  const done = await job.wait({ timeout: 30 * 60 * 1000 });
  console.log(done.data.result?.firstName, "credits:", done.data.creditsConsumed);
} catch (error) {
  if (error instanceof JobFailedError) {
    console.log("Job failed:", error.errorCode, "credits:", error.creditsConsumed);
  } else if (error instanceof JobTimeoutError) {
    console.log("Still running, resume later with client.jobs.wait:", error.jobId);
  } else {
    throw error;
  }
}
```

How `wait()` behaves:

- Each poll asks the API to hold the answer for up to 20 seconds (`pollWait`, 0 to 20), so the result arrives as soon as the job ends. Two polls start at least `Retry-After` seconds apart, 2 seconds when the header is missing.
- `timeout` is the overall deadline in milliseconds, 30 minutes by default. When it passes, `wait()` raises `JobTimeoutError` with `jobId` and `lastStatus`. The job may still finish: resume it with `client.jobs.wait(jobId)`.
- A job that ends with `status: "errored"` raises `JobFailedError`, with `errorCode`, `creditsConsumed`, `result` and `notFound`. The `notFound` field is `true` for `data-not-found`, `person-not-found`, `company-not-found`, `email-not-found` and `phone-not-found`.
- `onPoll` is called with each state the SDK reads, and `signal` stops the wait (see [Timeouts and cancellation](https://docs.reversecontact.com/sdk/javascript/timeouts)).
- The SDK never starts a new job after a failure. Your code decides whether to start another job, which can be billed again.

`client.jobs.retrieve(id, { wait: 20 })` reads one state of a job without waiting for the end. [Polling](https://docs.reversecontact.com/guides/polling) describes the job lifecycle and how long a result stays available.

### Webhook delivery instead of polling

Pass `webhookUrl` when you create the job to receive the result at that HTTPS URL as `{ webhookId, data }`. Use `parseWebhookDelivery` to check the payload shape and read `webhookId`, `data` and `isError`. The `isError` field is `true` when `data.errorCode` is a non-empty string. The helper does not validate the profile itself.

**Webhook delivery**

```typescript [TypeScript SDK]
import ReverseContact, { parseWebhookDelivery } from "@reverse-contact/sdk";

const client = new ReverseContact({ apiKey: process.env.RC_API_KEY });

const job = await client.fetch.personLive({
  url: "https://social.com/in/janedoe",
  webhookUrl: "https://your-app.com/webhooks/reversecontact"
});
console.log("Store this id to match the delivery:", job.id);

// The route behind webhookUrl, as a Fetch API handler (Bun, Deno, edge runtimes).
async function handleWebhook(request: Request): Promise<Response> {
  const delivery = parseWebhookDelivery(await request.json());
  if (delivery.isError) console.log(delivery.webhookId, "failed:", delivery.data);
  else console.log(delivery.webhookId, "result:", delivery.data);
  // Answer within 10 seconds, then process the data: deduplicate on webhookId.
  return new Response(null, { status: 200 });
}
```

A job created with `webhookUrl` can still be polled with `job.wait()`. See [Webhooks](https://docs.reversecontact.com/guides/webhooks) for delivery failures and retries.

## Read a job without waiting for completion

`job.retrieve({ wait: 20 })` reads one snapshot of the same job. `job.status` holds the initial acknowledgement status. Read `snapshot.data.status` for the current state. To retrieve or wait for a saved job id, use the `retrieve()` and `wait()` methods in the [client.jobs reference](https://docs.reversecontact.com/sdk/javascript/jobs).

**Verify an email**

```typescript [TypeScript SDK]
import ReverseContact from "@reverse-contact/sdk";

const client = new ReverseContact();
const job = await client.contact.verifyEmail({ email: "jane@acme.com" });
const snapshot = await job.retrieve({ wait: 20 });
console.log(snapshot.data.status);
const done = await job.wait();
console.log(done.data.result?.status);
```

Verify Email returns `email` and a `status` of `valid`, `risky`, `invalid` or `unknown`. Find Email and Find Phone return arrays of `{ value, type }`. See [client.contact](https://docs.reversecontact.com/sdk/javascript/contact) and [client.fetch](https://docs.reversecontact.com/sdk/javascript/fetch) for their parameters.

`parseWebhookDelivery` raises `ReverseContactError` if the delivery envelope is invalid. List operations and Bulk jobs use separate wait methods, described in [Lists and Bulk enrichment](https://docs.reversecontact.com/sdk/javascript/lists-and-bulk).

Return to the [TypeScript SDK overview](https://docs.reversecontact.com/sdk/javascript).

## FAQ

### How do I wait for a live profile or Find Email result?

Call `job.wait()` on the `Job` returned by a live fetch or contact lookup. The SDK polls for free, for up to 30 minutes by default.

### How do I resume waiting after my process restarts?

Save `job.id` before waiting, then call `client.jobs.wait(jobId)` after restarting. Resuming a job avoids creating another paid lookup.

### What happens when an asynchronous SDK job fails?

`job.wait()` raises `JobFailedError` with the job error code, credits consumed and any result. The SDK does not start a replacement job.

### Can I poll a job that also uses a webhook?

A job created with `webhookUrl` can still be polled with `job.wait()`. `parseWebhookDelivery` checks the callback envelope and identifies error deliveries.
