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.
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 }.