Direct Movement — Drop Off Parts to Repair Shop
Endpoint (all steps below, except the image upload)
One endpoint, event_type selects the operation. Same URL for every doc in this spec.
- Dev:
https://www.zohoapis.com.au/creator/custom/clientportal/Mobile_Kratos_Incoming_handler_DEV?publickey=xxxxxxxxxxxxxxxxxxxx - Method: POST
Step 1 — Parts_search_details (movement_type: "direct")
Payload
{
"payload": {
"event_type": "Parts_search_details",
"user_email": "vignesh.au.2024@gmail.com",
"details": {
"rego": "TN25AU3490",
"movement_type": "direct"
}
}
}
| Field | Type | Required | Notes |
|---|---|---|---|
rego | string | ✓ | Matched case-insensitively against Claim_Form.Customer_Registration |
Criteria (server-side)
claim_rec = Claim_Form where Customer_Registration contains rego
→ not found: error "Rego not found"
claims_management_rec = Claims_Management_Form linked to claim_rec
→ not found: error "Record not found"
parts_list = Parts_Order for this claim where Status == "Arrived"
→ empty: error "No parts ready for delivery to a repair shop"
repairer = claims_management_rec.Repairer
Response
{
"result": {
"data": {
"repairer": 10875000022297868,
"form_name": "pickup",
"movement_type": "direct",
"rego": "TN25AU3490",
"parts_list": [
{
"parts_order_id": 10875000030088709,
"description": "Testing",
"order_status": "Arrived"
}
]
},
"status": "success"
},
"code": 3000
}
repairer is auto-populated and read-only in the mobile UI — no location picker.
Step 2 — Upload Image (per part) — two calls
Two calls per photo. The staging form Parts_Uploaded_Images exists only to give mobile somewhere to put the raw file before the real Deliver_Parts record exists (that record isn't created until Step 3 submits). It is not the final home for the photo: Step 3 copies the image out of this staging record into the real target, the Deliver_Parts subform's Pickup_Image / Delivery_Image fields. Treat Parts_Uploaded_Images rows as disposable once copied.
Mobile never calls Zoho's native Add-Records API. Call 2a is the usual custom event on the shared handler — Creator creates the staging record and mints a short-lived OAuth token in the same call. Call 2b is the only raw-Zoho call in the whole flow.
Parts_Uploaded_Images fields
| Field | Type | Notes |
|---|---|---|
Parts_Order | picklist → Parts_Order.ID | Which part this photo is for |
Claim_Form | picklist → Claim_Form.ID | Resolved server-side from rego |
Movement_Stage | picklist | "direct_dropoff" for this doc |
Parts_Image | image | The photo (note: Parts_Image, not Image) |
Uploaded_By | picklist → User.ID | Resolved from user_email |
Uploaded_Time | datetime | Server-set |
Call 2a — parts_movement_upload_token
Same shared handler endpoint as every other event. Note the fields sit directly on payload, not nested under details.
{
"payload": {
"event_type": "parts_movement_upload_token",
"Parts_Order": "10875000024191015",
"Movement_Stage": "direct_dropoff",
"rego": "TN25AU3490",
"user_email": "user@email.com"
}
}
Response
{
"result": {
"data": {
"app_owner": "clientportal",
"app_name": "test-claims-management-portal",
"form_name": "Parts_Uploaded_Images",
"report_name": "Parts_Movement_Uploaded_Images_Temp_Report",
"field_name": "Parts_Image",
"record_id": 10875000030314834,
"token": "xxxxxxx",
"environment": true
},
"status": "success"
},
"code": 3000
}
record_idis theimage_idto carry into Step 3 — nothing from Call 2b needs capturing.environmentis only present in stage/development; omitted in production, same as the Fuel Receipt flow.- The token is opaque and short-lived — mint one per upload, never store it on the device.
Call 2b — attach the file
POST https://www.zohoapis.com.au/creator/v2.1/data/{app_owner}/{app_name}/report/{report_name}/{record_id}/{field_name}/upload
Authorization: Zoho-oauthtoken <token from 2a>
environment: development # stage/dev only
Content-Type: multipart/form-data
file: <binary>
Uses the report link name (Parts_Movement_Uploaded_Images_Temp_Report), not the form link name.
Response
{
"code": 3000,
"data": { "filename": "download.jpeg", "message": "File Uploaded Successfully" }
}
This response does not repeat the record ID — use record_id from Call 2a.
Repeat both calls once per confirmed part — one photo each, not one for the whole trip.
Step 3 — parts_drop_off
Payload
{
"payload": {
"event_type": "parts_drop_off",
"user_email": "user@email.com",
"details": {
"rego": "TN42AU6373",
"delivery_location": { "id": "10875000020622521", "name": "Bankstown Muffler Fit" },
"movement_type": "direct",
"parts": [
{ "parts_order_id": "10875000024189993", "confirm": true, "image_id": "10875000030314834" }
]
}
}
}
| Field | Required | Notes |
|---|---|---|
rego | ✓ | From Step 1. The claim is resolved server-side — no claim_id is sent |
movement_type | ✓ | "direct" |
delivery_location | ✓ | { id, name } — the repairer picked from the repairer-list dropdown, not just passed straight back from Step 1 (Step 1's repairer doesn't always resolve, e.g. fleet/loan-car/private jobs with no linked claim) |
parts[] | ✓ | confirm: false rows are dropped. image_id is record_id from Step 2's Call 2a |
Business logic
Single transaction — both legs of the physical trip (HQ pickup, repair-shop drop-off) are recorded together, since the driver only opens the app once, already at the repair shop:
- Create one
Parts_Movement:Movement_Type = "direct",Movement_Status = "Parts Dropped off",Picked_Up_By/Dropped_Of_By= the same user,Pick_Up_Date_Time/Dropped_Off_Date_Time= the same timestamp,Repair_Shop=delivery_location.id(and/or.name, whichever the target field expects). - For each confirmed part: create
Deliver_Parts(Pickup = true,Delivery = true,QtyOrderedfrom the part,Parts_Movement= the new movement). Look up the staged row inParts_Uploaded_Imagesbyimage_id, then copy itsParts_Imageinto thisDeliver_Partsrecord'sPickup_Imagefield and itsDelivery_Imagefield — both, since this combined flow only ever captures one photo per part (there's no separate HQ-pickup photo to put inPickup_Imageotherwise). UpdateParts_Order.Status:Arrived → Delivered to Repair Shopdirectly. - Post the Cliq "Parts Delivered" announcement (Parts Movement + New Repair Jobs channels) — server-side, mobile does not call anything further.
- Once copied, the staging row in
Parts_Uploaded_Imagesis no longer needed — safe to delete or leave for audit, at the Creator side's discretion.
Response
{
"result": {
"data": {
"delivery_parts": [
{
"parts_order_id": 10875000024189993,
"delivery_id": 10875000030318098
},
{
"parts_order_id": 10875000024191015,
"delivery_id": 10875000030318102
}
],
"parts_movement_id": 10875000030318096
},
"status": "success"
},
"code": 3000
}
delivery_parts[] (not deliver_parts) with delivery_id (not deliver_parts_id); IDs are numbers, not strings.
Error cases
| Condition | result.text |
|---|---|
rego resolves to no claim | "Claim not found." |
No parts have confirm: true | "Select at least one part to deliver." |
A confirmed part has no matching image_id | "<Part No.> is missing its delivery photo." |
A part is no longer Arrived (raced by the Dashboard) | "<Part No.> is no longer available for delivery." |