Skip to content

Jobs

A Job is the asynchronous unit of work behind every bulk write in this API. When you POST /articles, POST /customers, or POST /suppliers, Mercura validates the payload, persists an ImportRun row, enqueues a worker task, and returns a JobAck immediately. The actual ingestion (validation per row, upsert into the catalogue, error aggregation) happens out-of-band.

Polling GET /jobs/{job_id} is how you learn what happened.

The async write loop

POST /articles
Authorization: Bearer mrc_live_...
Idempotency-Key: nightly-sync-2026-05-19
Content-Type: application/json
{ "articles": [ ... up to 100,000 rows ... ] }

→  202 Accepted
   { "job_id": "4242", "status_url": "/api/public/v1/jobs/4242" }

Then poll:

GET /api/public/v1/jobs/4242

→  200 OK
   {
     "job_id": "4242",
     "entity": "ARTICLES",
     "status": "RUNNING",
     "created_at": "2026-05-19T10:00:00Z",
     "updated_at": "2026-05-19T10:00:05Z",
     "total_rows": 12000,
     "created_count": 0,
     "updated_count": 0,
     "skipped_count": 0,
     "deleted_count": 0,
     "error_count": 0,
     "errors": [],
     "result": null
   }

…and again a few seconds later:

{
   "job_id": "4242",
   "entity": "ARTICLES",
   "status": "COMPLETED",
   "created_at": "2026-05-19T10:00:00Z",
   "updated_at": "2026-05-19T10:00:42Z",
   "total_rows": 12000,
   "created_count": 9000,
   "updated_count": 2950,
   "skipped_count": 0,
   "deleted_count": 0,
   "error_count": 50,
   "errors": [
     { "row_number": 137, "identifier": "LEU-0900-18-830", "error_message": "missing list_price" }
   ],
   "result": null
}

result is null for ARTICLES / CUSTOMERS / SUPPLIERS jobs; for REQUESTS jobs (created via POST /tenders / POST /orders) it carries { request_id, request_status, position_count }.