Hazchem On The Go
Hazchem Connect Developers

API reference

Base URL https://api.haz-chem.co.za/v1. Endpoints marked Sandbox can be called from the console in this build.

Quotes
POST

/v1/quotes

quotes:writeSandbox

Price a job from the price book: SKU, distance, priority, timing and parcels. Quotes hold for 15 minutes.

ParameterInTypeDescription
sku *bodystringPrice-book SKU, e.g. HZ-LAB-04 (same-day specimen)
kmbodynumberRoute distance. Extras apply beyond the SKU's included km
prioritybodyenumscheduled | same_day | stat | critical. STAT upgrades lab and blood SKUs
timingbodyenumstandard | after_hours (x1.5) | saturday (x1.3) | sunday (x2.0)
parcelsbodyintegerR25.00 per additional parcel to the same address
Request
curl -X POST https://api.haz-chem.co.za/v1/quotes \
  -H "Authorization: Bearer $HZ_TOKEN" \
  -H "content-type: application/json" \
  -d '{"sku":"HZ-LAB-04","km":25,"priority":"same_day","timing":"standard","parcels":1}'
Body (JSON)
{
  "sku": "HZ-LAB-04",
  "km": 25,
  "priority": "same_day",
  "timing": "standard",
  "parcels": 1
}
Response 201
{
  "object": "quote",
  "id": "q_8k2m4p1x9z",
  "expires_at": "2026-10-12T09:45:00.000Z",
  "sku": "HZ-LAB-04",
  "priority": "same_day",
  "km": 25,
  "lines": [
    {
      "label": "Same-day specimen (4 h window), metro, incl. 15 km",
      "amount_cents": 18500
    },
    {
      "label": "Distance 10 km beyond 15 km at R9.00/km",
      "amount_cents": 9000
    }
  ],
  "amount_cents": 27500,
  "vat_cents": 4125,
  "total_cents": 31625,
  "currency": "ZAR",
  "vat_inclusive": false,
  "sla_minutes": 240,
  "price_book_version": "2026.10-v1.0"
}
GET

/v1/quotes

quotes:writeSandbox

List recent quotes and the quotable SKUs in the current price book.

Request
curl https://api.haz-chem.co.za/v1/quotes \
  -H "Authorization: Bearer $HZ_TOKEN"
Response 200
{
  "object": "list",
  "price_book_version": "2026.10-v1.0",
  "data": [
    {
      "id": "q_8k2m4p1x9z",
      "sku": "HZ-LAB-04",
      "total_cents": 31625
    }
  ],
  "quotable_skus": [
    {
      "sku": "HZ-LAB-04",
      "name": "Same-day specimen (4 h window), metro, incl. 15 km",
      "unit": "per job",
      "price_cents": 18500
    }
  ]
}
Jobs
POST

/v1/jobs

jobs:writeSandbox

Create a job from a quote or directly. Idempotency-Key is required; a repeated key returns the original job for 24 h.

ParameterInTypeDescription
Idempotency-Key *headerstringUnique per booking attempt, e.g. a UUID
quote_idbodystringBook an existing quote, or send sku/priority/km instead
pickup_site_id *bodystringSandbox sites: cli-0..8, lab-0..3, hos-0..3, ph-0..11, whl-0, bb-0
dropoff_site_id *bodystringDestination site id
cost_centrebodystringCost centre or expense code for the invoice
Request
curl -X POST https://api.haz-chem.co.za/v1/jobs \
  -H "Authorization: Bearer $HZ_TOKEN" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "content-type: application/json" \
  -d '{"sku":"HZ-LAB-05","priority":"stat","pickup_site_id":"cli-0","dropoff_site_id":"lab-0","cost_centre":"STAT"}'
Body (JSON)
{
  "sku": "HZ-LAB-05",
  "priority": "stat",
  "pickup_site_id": "cli-0",
  "dropoff_site_id": "lab-0",
  "cost_centre": "STAT"
}
Response 201
{
  "id": "job-sbx1",
  "ref": "HZ-SBX1001",
  "object": "job",
  "status": "booked",
  "sku": "HZ-LAB-05",
  "service": "STAT specimen (90-minute)",
  "priority": "stat",
  "tenant": {
    "id": "t-lab",
    "name": "Meridian Pathology (sample)"
  },
  "pickup": {
    "site_id": "cli-0",
    "name": "Laudium Family Practice"
  },
  "dropoff": {
    "site_id": "lab-0",
    "name": "Meridian Central Lab"
  },
  "parcels": 1,
  "seal": null,
  "un_number": "UN3373",
  "temperature_range_c": null,
  "km": 22,
  "created_at": "2026-10-12T09:30:00.000Z",
  "sla_due": "2026-10-12T11:00:00.000Z",
  "delivered_at": null,
  "cost_centre": "STAT",
  "price": {
    "amount_cents": 43100,
    "currency": "ZAR",
    "vat_inclusive": false
  },
  "tracking_url": "https://portal.haz-chem.co.za/track/HZ-SBX1001",
  "certificate_url": null,
  "webhook_events": [
    "job.created"
  ]
}
GET

/v1/jobs

jobs:readSandbox

List and filter jobs.

ParameterInTypeDescription
statusqueryenumbooked | assigned | en_route_pickup | collected | in_transit | delivered | exception | cancelled
sitequerystringPickup or drop-off site id
limitqueryinteger1 to 100, default 20
Request
curl https://api.haz-chem.co.za/v1/jobs \
  -H "Authorization: Bearer $HZ_TOKEN"
Response 200
{
  "object": "list",
  "total": 16,
  "data": [
    {
      "id": "job-sbx1",
      "ref": "HZ-SBX1001",
      "object": "job",
      "status": "delivered",
      "sku": "HZ-LAB-05",
      "service": "STAT specimen (90-minute)",
      "priority": "stat",
      "tenant": {
        "id": "t-lab",
        "name": "Meridian Pathology (sample)"
      },
      "pickup": {
        "site_id": "cli-0",
        "name": "Laudium Family Practice"
      },
      "dropoff": {
        "site_id": "lab-0",
        "name": "Meridian Central Lab"
      },
      "parcels": 1,
      "seal": null,
      "un_number": "UN3373",
      "temperature_range_c": null,
      "km": 22,
      "created_at": "2026-10-12T09:30:00.000Z",
      "sla_due": "2026-10-12T11:00:00.000Z",
      "delivered_at": null,
      "cost_centre": "STAT",
      "price": {
        "amount_cents": 43100,
        "currency": "ZAR",
        "vat_inclusive": false
      },
      "tracking_url": "https://portal.haz-chem.co.za/track/HZ-SBX1001",
      "certificate_url": null
    }
  ],
  "has_more": true
}
GET

/v1/jobs/{id}

jobs:readSandbox

Retrieve one job by id or ref, with legs, parcels and status.

ParameterInTypeDescription
id *pathstringJob id (job-11) or reference (HZ-261012070)
Request
curl https://api.haz-chem.co.za/v1/jobs/job-11 \
  -H "Authorization: Bearer $HZ_TOKEN"
Response 200
{
  "id": "job-sbx1",
  "ref": "HZ-SBX1001",
  "object": "job",
  "status": "booked",
  "sku": "HZ-LAB-05",
  "service": "STAT specimen (90-minute)",
  "priority": "stat",
  "tenant": {
    "id": "t-lab",
    "name": "Meridian Pathology (sample)"
  },
  "pickup": {
    "site_id": "cli-0",
    "name": "Laudium Family Practice"
  },
  "dropoff": {
    "site_id": "lab-0",
    "name": "Meridian Central Lab"
  },
  "parcels": 1,
  "seal": null,
  "un_number": "UN3373",
  "temperature_range_c": null,
  "km": 22,
  "created_at": "2026-10-12T09:30:00.000Z",
  "sla_due": "2026-10-12T11:00:00.000Z",
  "delivered_at": null,
  "cost_centre": "STAT",
  "price": {
    "amount_cents": 43100,
    "currency": "ZAR",
    "vat_inclusive": false
  },
  "tracking_url": "https://portal.haz-chem.co.za/track/HZ-SBX1001",
  "certificate_url": null,
  "custody_events": 1
}
POST

/v1/jobs/{id}/cancel

jobs:writeSandbox

Cancel with a reason. Free before assignment; 50% of base once a driver is assigned. Collected parcels need a return job instead.

ParameterInTypeDescription
id *pathstringJob id or ref
reason *bodystringShown to Control and on the invoice
Request
curl -X POST https://api.haz-chem.co.za/v1/jobs/job-11/cancel \
  -H "Authorization: Bearer $HZ_TOKEN" \
  -H "content-type: application/json" \
  -d '{"reason":"Specimen not ready"}'
Body (JSON)
{
  "reason": "Specimen not ready"
}
Response 200
{
  "id": "job-sbx1",
  "ref": "HZ-SBX1001",
  "object": "job",
  "status": "cancelled",
  "sku": "HZ-LAB-05",
  "service": "STAT specimen (90-minute)",
  "priority": "stat",
  "tenant": {
    "id": "t-lab",
    "name": "Meridian Pathology (sample)"
  },
  "pickup": {
    "site_id": "cli-0",
    "name": "Laudium Family Practice"
  },
  "dropoff": {
    "site_id": "lab-0",
    "name": "Meridian Central Lab"
  },
  "parcels": 1,
  "seal": null,
  "un_number": "UN3373",
  "temperature_range_c": null,
  "km": 22,
  "created_at": "2026-10-12T09:30:00.000Z",
  "sla_due": "2026-10-12T11:00:00.000Z",
  "delivered_at": null,
  "cost_centre": "STAT",
  "price": {
    "amount_cents": 43100,
    "currency": "ZAR",
    "vat_inclusive": false
  },
  "tracking_url": "https://portal.haz-chem.co.za/track/HZ-SBX1001",
  "certificate_url": null,
  "cancellation": {
    "reason": "Specimen not ready",
    "fee_cents": 0,
    "currency": "ZAR",
    "rule": "Cancelled before assignment: no fee"
  },
  "webhook_events": [
    "job.cancelled"
  ]
}
Tracking
GET

/v1/tracking/{ref}

tracking:readSandbox

Tracking URL, ETA, driver first name and photo, vehicle and custody milestones. Never includes medicine names.

ParameterInTypeDescription
ref *pathstringJob reference
Request
curl https://api.haz-chem.co.za/v1/tracking/HZ-261012000 \
  -H "Authorization: Bearer $HZ_TOKEN"
Response 200
{
  "ref": "HZ-261012000",
  "status": "in_transit",
  "tracking_url": "https://portal.haz-chem.co.za/track/HZ-261012000",
  "eta_minutes": 22,
  "driver": {
    "first_name": "Sipho"
  },
  "vehicle": {
    "registration": "THE GO 40 GP",
    "last_position": {
      "lat": -26.0193,
      "lng": 27.8903
    }
  },
  "events": [
    {
      "at": "2026-10-12T09:41:00.000Z",
      "type": "sealed",
      "note": "Tamper seal applied",
      "seal": "HZ-S445330"
    }
  ]
}
Proof
GET

/v1/jobs/{id}/custody

custody:readLive only

Hash-chained custody events with seals, positions and actors. Live API only.

Request
curl https://api.haz-chem.co.za/v1/jobs/job-11/custody \
  -H "Authorization: Bearer $HZ_TOKEN"
Response 200
{
  "job_id": "job-11",
  "chain_hash": "sha256:7b91e13b...",
  "events": [
    {
      "at": "2026-10-12T08:12:00.000Z",
      "type": "scanned",
      "by": "Driver",
      "note": "2 parcel(s) scanned",
      "prev_hash": "sha256:19ac...",
      "hash": "sha256:a4f0..."
    }
  ]
}
GET

/v1/jobs/{id}/pod

pod:readLive only

Proof of delivery: recipient, signature and photo links, ID or OTP result. Live API only.

Request
curl https://api.haz-chem.co.za/v1/jobs/job-11/pod \
  -H "Authorization: Bearer $HZ_TOKEN"
Response 200
{
  "job_id": "job-11",
  "recipient": "Receiving desk",
  "otp_verified": true,
  "id_check": "over_14_confirmed",
  "signature_url": "https://files.haz-chem.co.za/pod/...sig.png",
  "photo_url": "https://files.haz-chem.co.za/pod/...jpg",
  "delivered_at": "2026-10-12T10:02:00.000Z"
}
GET

/v1/certificates/{id}

certificates:readSandbox

Proof-of-Custody (POC-) or Proof-of-Cold-Chain (PCC-) certificate JSON with a public, redacted verify link.

ParameterInTypeDescription
id *pathstringPOC-0011, PCC-0012 or a delivered job id
Request
curl https://api.haz-chem.co.za/v1/certificates/job-11 \
  -H "Authorization: Bearer $HZ_TOKEN"
Response 200
{
  "id": "POC-0011",
  "object": "certificate",
  "type": "proof_of_custody",
  "job_id": "job-11",
  "issued_at": "2026-10-12T16:01:55.446Z",
  "custody": {
    "events": 7,
    "seal": "HZ-S445330",
    "chain_hash": "sha256:7b91e13b..."
  },
  "verify_url": "https://portal.haz-chem.co.za/verify/job-11"
}
Organisation
GET

/v1/sites

admin:sitesLive only

Sites, receiving hours and cost centres for your organisation. Live API only.

Request
curl https://api.haz-chem.co.za/v1/sites \
  -H "Authorization: Bearer $HZ_TOKEN"
Response 200
{
  "object": "list",
  "data": [
    {
      "id": "lab-0",
      "name": "Meridian Central Lab",
      "type": "lab",
      "receiving_hours": "24/7",
      "cost_centres": [
        "Routes - Tshwane"
      ]
    }
  ]
}
POST

/v1/webhooks

webhooks:manageLive only

Register a webhook endpoint for selected events. The signing secret is shown once.

Request
curl -X POST https://api.haz-chem.co.za/v1/webhooks \
  -H "Authorization: Bearer $HZ_TOKEN" \
  -H "content-type: application/json" \
  -d '{"url":"https://lis.example.co.za/hazchem/events","events":["job.assigned","pickup.completed","delivery.completed","temperature.excursion"]}'
Body (JSON)
{
  "url": "https://lis.example.co.za/hazchem/events",
  "events": [
    "job.assigned",
    "pickup.completed",
    "delivery.completed",
    "temperature.excursion"
  ]
}
Response 201
{
  "id": "wh_3f9a",
  "url": "https://lis.example.co.za/hazchem/events",
  "events": [
    "job.assigned",
    "pickup.completed",
    "delivery.completed",
    "temperature.excursion"
  ],
  "secret": "whsec_test_...",
  "status": "active"
}
Platform
GET

/v1/health

publicSandbox

Service status. No key needed.

Request
curl https://api.haz-chem.co.za/v1/health \
  -H "Authorization: Bearer $HZ_TOKEN"
Response 200
{
  "status": "ok",
  "environment": "sandbox",
  "api_version": "v1",
  "price_book_version": "2026.10-v1.0"
}