Driver documents (KYC)

The driver onboarding flow: upload KYC documents, register the vehicle, submit for review, and check the verification status. Paths are relative to the /api/v1 prefix; every route requires Authorization: Bearer {token} and the DRIVER role.

See Common errors and Fleet & KYC for how drivers, vehicles, and documents relate.

Endpoints

Method URI Role Description
POST /driver/documents DRIVER Upload (or replace) a KYC document
GET /driver/vehicle DRIVER Get the driver's vehicle
PATCH /driver/vehicle DRIVER Create or update the driver's vehicle
POST /driver/kyc/submit DRIVER Submit KYC for admin review
GET /driver/kyc/status DRIVER Current KYC state, documents, and gaps

KYC states are PENDING_KYC, PENDING_REVIEW, APPROVED, and REJECTED. Only ID_FRONT and LICENSE are required to submit; a vehicle is also required.


POST /driver/documents

Uploads a KYC document as multipart/form-data. Re-uploading the same type replaces it, and the document's status returns to PENDING.

Request body (multipart/form-data)

Field Type Required Rules
type string yes one of ID_FRONT, ID_BACK, LICENSE, MEDICAL_CERT, RCV, ADDRESS_PROOF, VEHICLE_PHOTO, SANITARY_CERT
file file yes ≤ 10 MB; jpg, jpeg, png, pdf, or webp
expires_at date conditional must be after today; required for LICENSE, MEDICAL_CERT, RCV, SANITARY_CERT

Response 201

{
  "document": {
    "id": 17,
    "type": "LICENSE",
    "url": "https://…/storage/kyc/3/LICENSE-20260626120000.jpg",
    "status": "PENDING",
    "expires_at": "2027-06-26T00:00:00+00:00"
  }
}

Errors

Status Body When
404 { "error": "DriverNotFound" } The caller has no driver profile
422 { "error": "ExpiresAtRequired", "message": "…" } The document type requires expires_at and none was given

GET /driver/vehicle

Returns the driver's single vehicle.

Response 200

{
  "vehicle": {
    "id": 4,
    "driver_id": 3,
    "plate": "ABC-123",
    "capacity_liters": 5000,
    "brand": "Ford",
    "model": "Cargo",
    "year": 2018,
    "hose_length_meters": 30,
    "has_motopump": true,
    "operation_radius_km": 15,
    "photo_urls": [],
    "sanitary_cert_url": null,
    "cert_expires_at": null,
    "status": "PENDING",
    "created_at": "2026-06-20T10:00:00.000000Z",
    "updated_at": "2026-06-20T10:00:00.000000Z"
  }
}

Errors

Status Body When
404 { "error": "DriverNotFound" } The caller has no driver profile
404 { "error": "VehicleNotFound" } The driver has no vehicle registered

PATCH /driver/vehicle

Creates or updates the driver's single vehicle. The plate must be unique across drivers.

Request body

Field Type Required Rules
plate string yes ≤ 20 chars
capacity_liters integer yes 500–50000
brand string no ≤ 60 chars
model string no ≤ 60 chars
year integer no 1980 – next year
hose_length_meters integer no 1–100
has_motopump boolean no
operation_radius_km integer no 1–100
sanitary_cert_url string no valid URL, ≤ 500 chars
cert_expires_at date no after today

Response 200

{
  "vehicle": {
    "id": 4,
    "driver_id": 3,
    "plate": "ABC-123",
    "capacity_liters": 5000,
    "brand": "Ford",
    "model": "Cargo",
    "year": 2018,
    "hose_length_meters": 30,
    "has_motopump": true,
    "operation_radius_km": 15,
    "status": "PENDING",
    "created_at": "2026-06-20T10:00:00+00:00"
  }
}

Errors

Status Body When
404 { "error": "DriverNotFound" } The caller has no driver profile
409 { "error": "PlateAlreadyUsed", "message": "…" } The plate is registered to another driver

POST /driver/kyc/submit

Submits KYC for admin review. Requires the vehicle and all required documents to be present, and KYC not to be already submitted or approved.

Response 200

{ "kyc": { "state": "PENDING_REVIEW", "submittedAt": "2026-06-26T13:00:00+00:00" } }

Errors

Status Body When
404 { "error": "DriverNotFound" } The caller has no driver profile
409 { "error": "WrongKycState", "message": "…", "current": "PENDING_REVIEW" } KYC is already submitted or approved
422 { "error": "VehicleMissing", "message": "…" } No vehicle registered
422 { "error": "DocumentsMissing", "message": "…", "missing": ["LICENSE"] } Required documents are missing

GET /driver/kyc/status

The current KYC state, the uploaded documents, and what is still missing to submit.

Response 200

{
  "state": "PENDING_KYC",
  "reviewNote": null,
  "submittedAt": null,
  "reviewedAt": null,
  "documents": [
    {
      "type": "ID_FRONT",
      "status": "PENDING",
      "expires_at": null,
      "reviewNote": null
    }
  ],
  "requiredMissing": ["LICENSE"],
  "hasVehicle": false
}

reviewNote carries the admin's note when the state is REJECTED. requiredMissing lists the required document types not yet uploaded.

Errors

Status Body When
404 { "error": "DriverNotFound" } The caller has no driver profile

Examples

# Upload the driver's license (requires an expiry date)
curl -X POST /api/v1/driver/documents \
  -H "Authorization: Bearer {driver_token}" \
  -F "type=LICENSE" \
  -F "expires_at=2027-06-26" \
  -F "file=@license.jpg"

# Register the vehicle
curl -X PATCH /api/v1/driver/vehicle \
  -H "Authorization: Bearer {driver_token}" \
  -H "Content-Type: application/json" \
  -d '{"plate": "ABC-123", "capacity_liters": 5000}'

# Read the vehicle
curl /api/v1/driver/vehicle -H "Authorization: Bearer {driver_token}"

# Submit for review, then poll status
curl -X POST /api/v1/driver/kyc/submit -H "Authorization: Bearer {driver_token}"
curl /api/v1/driver/kyc/status -H "Authorization: Bearer {driver_token}"