Sales Engine
API documentation
Guides

Jobs and polling

Sourcing, research, copy generation, launch, scans and polls take longer than a request should. Each returns a job at once and does the work in the background; you read the job until it settles.

The shape

GET /jobs/{id}
{
  "data": {
    "id": "…",
    "type": "lead_list.build",
    "status": "running",
    "progress": { "step": "enrich", "done": 31, "total": 50, "message": "…" },
    "children_total": 0,
    "children_done": 0,
    "result": null,
    "error": null,
    "estimated_cost_usd": 1.9,
    "actual_cost_usd": 1.2,
    "created_at": "…",
    "started_at": "…",
    "finished_at": null,
    "events": [ { "id": 1, "level": "info", "message": "…", "meta": {}, "created_at": "…" } ]
  }
}
StatusMeaning
queuedAccepted, waiting for a worker.
runningA worker holds it; progress and events move.
doneFinished. result holds what it produced and actual_cost_usd what it spent.
failedGave up after its retries. error says why, in plain words.
cancelledStopped on purpose.

Where the job id comes from

Waiting well

Poll the job with a growing gap: two seconds, then five, then ten, up to a minute. A list of 50 leads with verification and enrichment takes a few minutes; research of a large company can take ten. Every poll counts against the rate limit, so a tight loop wastes your budget for nothing.

Shell
JOB=…
until [ "$(curl -s https://crm.ashwanth.dev/api/v1/jobs/$JOB -H "Authorization: Bearer $SALES_ENGINE_API_KEY" | jq -r .data.status)" != "running" ]; do sleep 5; done

The CLI does this for you with sales-engine wait <job id>, and the MCP server exposes it as wait_for_job.

Queueing one by hand

Queue a job by type for the cases the resource endpoints do not cover. The type names are the ones the jobs list shows; the payload is what that type reads.