diff --git a/postman/collections/Fleetbase API/.resources/definition.yaml b/postman/collections/Fleetbase API/.resources/definition.yaml index de291f3..09fcd52 100644 --- a/postman/collections/Fleetbase API/.resources/definition.yaml +++ b/postman/collections/Fleetbase API/.resources/definition.yaml @@ -57,6 +57,10 @@ variables: driver_password_reset_code: "" manifest_id: "" manifest_stop_id: "" + # Forms are published from the console, so List Inspection Forms chains the + # first published one; a contract run has to seed it. + inspection_form_id: "" + inspection_id: "" # Fleet hierarchy. The parent is the fleet created first; the subfleet is # created beneath it and deleted before it. parent_fleet_id: "" diff --git a/postman/collections/Fleetbase API/Inspections/.resources/definition.yaml b/postman/collections/Fleetbase API/Inspections/.resources/definition.yaml new file mode 100644 index 0000000..a1bab11 --- /dev/null +++ b/postman/collections/Fleetbase API/Inspections/.resources/definition.yaml @@ -0,0 +1,8 @@ +$kind: collection +description: |- + Inspections are the driver's vehicle inspection reports — a DVIR filed before or after a trip against a form the organisation has published from the console. + + These endpoints are for the driver filing the inspection: read the published forms, submit against one, and read back what was filed and what it produced. A failed item can raise an issue and open a work order, depending on how the form is configured; both come back on the submission. + + Authoring and publishing forms, and reviewing or resolving submissions, is fleet management and is not part of the consumable API. The requests here need at least one published form in the organisation, which a contract run has to seed. +order: 7750 diff --git a/postman/collections/Fleetbase API/Inspections/.resources/object.yaml b/postman/collections/Fleetbase API/Inspections/.resources/object.yaml new file mode 100644 index 0000000..fa26eea --- /dev/null +++ b/postman/collections/Fleetbase API/Inspections/.resources/object.yaml @@ -0,0 +1,180 @@ +$kind: object +name: Inspection +description: |- + An inspection is one filed report against a published inspection form: the vehicle and driver it was filed for, the driver's answer to every field of the form, the pass/fail results derived from the `pass-fail` answers, the photos and signature filed with it, and the issue or work order the form's settings raised from any failed items. +example: | + { + "id": "inspection_submission_4Mnp8Qx2Vz", + "form": { "id": "inspection_form_9Kd2Lm7Aq1", "name": "Pre-trip DVIR", "type": "dvir" }, + "vehicle": { "id": "vehicle_7Hx3Pq9Rt2", "plate_number": "TRK-7" }, + "driver": { "id": "driver_2Fb8Nc4Wm6", "name": "Dana Driver" }, + "issue": null, + "work_order": null, + "custom_field_values": [ + { + "custom_field": "0d2b7f1e-4a3c-4d5e-9f61-2c8b7a6d5e4f", + "name": "brakes", + "label": "Brakes", + "type": "pass-fail", + "value_type": "object", + "value": { + "passed": true, + "not_applicable": false, + "severity": null, + "comments": null, + "photos": [], + "unsafe": false + } + }, + { + "custom_field": "7c1a9e42-6b8d-4f30-8a52-1d9f3b6c4e70", + "name": "odometer", + "label": "Odometer", + "type": "number", + "value_type": "number", + "value": 120400 + }, + { + "custom_field": "b8e5d3c1-2f74-4a96-bc08-5e1a7d2f9c63", + "name": "signature", + "label": "Sign here", + "type": "signature", + "value_type": "file", + "value": { + "id": "file_5Qw8Zt2Nb7", + "url": "https://files.fleetbase.io/inspections/4Mnp8Qx2Vz/lq8w1c0v9xk3.png", + "filename": "lq8w1c0v9xk3.png", + "content_type": "image/png" + } + } + ], + "files": [ + { + "id": "file_5Qw8Zt2Nb7", + "uuid": "3a6c9e21-7b4d-4f18-9c05-2d8e1f7a6b34", + "url": "https://files.fleetbase.io/inspections/4Mnp8Qx2Vz/lq8w1c0v9xk3.png", + "original_filename": "lq8w1c0v9xk3.png", + "content_type": "image/png", + "type": "inspection_signature", + "caption": null, + "created_at": "2026-09-09T07:30:00.000000Z" + } + ], + "item_results": [ + { + "id": "b3f0c4a2-1d6e-4c1a-9c2b-8e7f6a5d4c3b", + "item_key": "brakes", + "label": "Brakes", + "category": "Safety", + "status": "passed", + "severity": null, + "passed": true, + "comments": null, + "photos": [], + "meta": {}, + "submission_id": "inspection_submission_4Mnp8Qx2Vz", + "updated_at": "2026-09-09T07:30:00.000000Z", + "created_at": "2026-09-09T07:30:00.000000Z" + } + ], + "type": "dvir", + "status": "submitted", + "result": "passed", + "source": "navigator", + "odometer": 120400, + "engine_hours": null, + "total_items": 1, + "failed_items": 0, + "location": { "latitude": 1.3521, "longitude": 103.8198 }, + "signature": {}, + "attachments": [], + "meta": {}, + "form_name": "Pre-trip DVIR", + "vehicle_name": "Truck 7", + "driver_name": "Dana Driver", + "has_failures": false, + "started_at": "2026-09-09T07:10:00.000000Z", + "submitted_at": "2026-09-09T07:30:00.000000Z", + "resolved_at": null, + "updated_at": "2026-09-09T07:30:00.000000Z", + "created_at": "2026-09-09T07:30:00.000000Z" + } +fields: + - name: id + type: string + description: Public inspection identifier. + - name: form + type: object + description: The inspection form the report was filed against, when loaded. + - name: vehicle + type: object + description: The vehicle inspected, when loaded. + - name: driver + type: object + description: The driver who filed the inspection, when loaded. + - name: issue + type: object + description: The issue raised from the failed items, when the form asks for one and any item failed. + - name: work_order + type: object + description: The work order opened for the failed items, when the form asks for one and any item failed. + - name: custom_field_values + type: array of objects + description: The driver's answer to each field of the form — the field's `custom_field` id, `name`, `label`, `type` and `value_type` beside the `value`. A `pass-fail` value is an object of `passed`, `not_applicable`, `severity`, `comments`, `photos` and `unsafe`; a photo or signature value is resolved to `{ id, url, filename, content_type }` rather than the base64 that was sent. + - name: files + type: array of objects + description: The photos and signatures filed with the inspection, however they arrived — inside the submit body, or dropped on the console's Photos panel. + - name: item_results + type: array of objects + description: One result per `pass-fail` answer, derived from `custom_field_values` — `item_key`, `label`, `category`, `status` (`passed`, `failed` or `not_applicable`), `severity`, `passed`, `comments` and `photos`. This is what issues, work orders and the vehicle's history are built from. + - name: type + type: string + description: Inspection type, taken from the form — `dvir`, `pre_trip`, `post_trip`, or a custom type. + - name: status + type: string + description: Lifecycle status — `submitted`, `needs_review` or `resolved`. + - name: result + type: string + description: "`passed` when every item passed, otherwise `failed`." + - name: source + type: string + description: How it was filed — `navigator` for the driver app, `public_link` for a tokenised link. + - name: odometer + type: number + description: Odometer reading at the time of the inspection. + - name: engine_hours + type: number + description: Engine hours at the time of the inspection. + - name: total_items + type: integer + description: Number of item results. + - name: failed_items + type: integer + description: Number of item results that did not pass. + - name: location + type: object + description: Where the inspection was filed from. + - name: signature + type: object + description: The driver's signature, as the app captured it. + - name: attachments + type: array + description: Additional files attached to the inspection. + - name: has_failures + type: boolean + description: Whether any item failed. + - name: started_at + type: timestamp + description: When the driver began the inspection. + - name: submitted_at + type: timestamp + description: When the inspection was filed. + - name: resolved_at + type: timestamp + description: When the failed items were resolved from the console, if they have been. + - name: updated_at + type: timestamp + description: Timestamp when the inspection was last updated. + - name: created_at + type: timestamp + description: Timestamp when the inspection was created. diff --git a/postman/collections/Fleetbase API/Inspections/List Inspection Forms.queryParams.yaml b/postman/collections/Fleetbase API/Inspections/List Inspection Forms.queryParams.yaml new file mode 100644 index 0000000..9e5e7a2 --- /dev/null +++ b/postman/collections/Fleetbase API/Inspections/List Inspection Forms.queryParams.yaml @@ -0,0 +1,14 @@ +$kind: queryParams +fields: + - name: vehicle + type: string + required: false + description: Only forms that apply to this vehicle — bound to it, or bound to no vehicle at all. + - name: type + type: string + required: false + description: Only forms of these types, such as `dvir` or `pre_trip,post_trip`. + - name: limit + type: integer + required: false + description: Maximum forms to return. Defaults to 30. diff --git a/postman/collections/Fleetbase API/Inspections/List Inspection Forms.request.yaml b/postman/collections/Fleetbase API/Inspections/List Inspection Forms.request.yaml new file mode 100644 index 0000000..6e338a2 --- /dev/null +++ b/postman/collections/Fleetbase API/Inspections/List Inspection Forms.request.yaml @@ -0,0 +1,27 @@ +$kind: http-request +name: "List Inspection Forms" +description: |- + Lists the inspection forms the organisation has published, newest first. Draft and archived forms are never listed: a driver can only file against a published one. + + Each form carries its structure as `grouped_fields` — the groups of typed fields a driver fills in — so a list is enough to render a form picker without reading each one. + + Send `vehicle` to narrow the list to what applies to one vehicle — forms bound to that vehicle, and forms bound to nothing, which are the organisation-wide ones. Send `type` to narrow by form type, as one value or a comma separated list. +url: "{{base_url}}/{{namespace}}/inspection-forms" +method: GET +queryParams: + vehicle: "{{vehicle_id}}" + +scripts: + - type: afterResponse + code: |- + // Forms are published from the console, not this API, so the first + // published form is what Retrieve an Inspection Form and Submit an + // Inspection address. A contract run has to seed one. + const forms = pm.response.json(); + + if (Array.isArray(forms) && forms.length) { + pm.environment.set("inspection_form_id", forms[0].id); + } + language: text/javascript + +order: 1000 diff --git a/postman/collections/Fleetbase API/Inspections/List Inspections.queryParams.yaml b/postman/collections/Fleetbase API/Inspections/List Inspections.queryParams.yaml new file mode 100644 index 0000000..9930016 --- /dev/null +++ b/postman/collections/Fleetbase API/Inspections/List Inspections.queryParams.yaml @@ -0,0 +1,26 @@ +$kind: queryParams +fields: + - name: driver + type: string + required: false + description: Only inspections filed by this driver. + - name: vehicle + type: string + required: false + description: Only inspections of this vehicle. + - name: type + type: string + required: false + description: Only inspections of these types, such as `dvir` or `pre_trip,post_trip`. + - name: result + type: string + required: false + description: "`passed` or `failed`." + - name: status + type: string + required: false + description: "`submitted`, `needs_review` or `resolved`." + - name: limit + type: integer + required: false + description: Maximum inspections to return. Defaults to 30. diff --git a/postman/collections/Fleetbase API/Inspections/List Inspections.request.yaml b/postman/collections/Fleetbase API/Inspections/List Inspections.request.yaml new file mode 100644 index 0000000..b2c609b --- /dev/null +++ b/postman/collections/Fleetbase API/Inspections/List Inspections.request.yaml @@ -0,0 +1,14 @@ +$kind: http-request +name: "List Inspections" +description: |- + Lists filed inspections, newest first. + + Narrow by `driver` or `vehicle`, and by `type`, `result` and `status`, each as one value or a comma separated list. Defaults to a recent window rather than the organisation's whole history; use `limit` to widen it. + + Each row carries the same shape Retrieve an Inspection answers with: the answers as `custom_field_values`, the `item_results` derived from them, and the `files` filed with the inspection. +url: "{{base_url}}/{{namespace}}/inspections" +method: GET +queryParams: + driver: "{{driver_id}}" + +order: 4000 diff --git a/postman/collections/Fleetbase API/Inspections/List Vehicle Inspections.queryParams.yaml b/postman/collections/Fleetbase API/Inspections/List Vehicle Inspections.queryParams.yaml new file mode 100644 index 0000000..b6ffeae --- /dev/null +++ b/postman/collections/Fleetbase API/Inspections/List Vehicle Inspections.queryParams.yaml @@ -0,0 +1,18 @@ +$kind: queryParams +fields: + - name: type + type: string + required: false + description: Only inspections of these types, such as `dvir` or `pre_trip,post_trip`. + - name: result + type: string + required: false + description: "`passed` or `failed`." + - name: status + type: string + required: false + description: "`submitted`, `needs_review` or `resolved`." + - name: limit + type: integer + required: false + description: Maximum inspections to return. Defaults to 30. diff --git a/postman/collections/Fleetbase API/Inspections/List Vehicle Inspections.request.yaml b/postman/collections/Fleetbase API/Inspections/List Vehicle Inspections.request.yaml new file mode 100644 index 0000000..ed78ec6 --- /dev/null +++ b/postman/collections/Fleetbase API/Inspections/List Vehicle Inspections.request.yaml @@ -0,0 +1,17 @@ +$kind: http-request +name: "List Vehicle Inspections" +description: |- + Lists a vehicle's inspection history, newest first — the same inspections List Inspections answers with `vehicle`, addressed the way the driver app holds them, from the vehicle's screen. + + Narrow by `type`, `result` and `status`, each as one value or a comma separated list. + + Each row carries the answers as `custom_field_values`, the `item_results` derived from them, and the `files` filed with the inspection. +url: "{{base_url}}/{{namespace}}/vehicles/:id/inspections" +method: GET +pathVariables: + - key: id + value: "{{vehicle_id}}" + description: (Required) The vehicle whose inspections to list. + + +order: 6000 diff --git a/postman/collections/Fleetbase API/Inspections/Retrieve an Inspection Form.request.yaml b/postman/collections/Fleetbase API/Inspections/Retrieve an Inspection Form.request.yaml new file mode 100644 index 0000000..081b44b --- /dev/null +++ b/postman/collections/Fleetbase API/Inspections/Retrieve an Inspection Form.request.yaml @@ -0,0 +1,69 @@ +$kind: http-request +name: "Retrieve an Inspection Form" +description: |- + Retrieves one published inspection form with its structure and `settings`. + + A form is groups of typed fields. `grouped_fields` is what a driver renders: one entry per group, in the order the builder laid them out, each with a `name`, a `meta.grid_size` and its `fields`. A field carries an `id`, a `name`, a `label`, a `type` — `pass-fail`, `input`, `textarea`, `number`, `select`, `radio-button`, `boolean`, `date-picker`, `date-time-input`, `file-upload` or `signature` — plus `required`, `options` and `meta`. + + A `pass-fail` field's `meta` says what happens when it fails: the default `severity`, whether a photo or a comment is required, whether the defect takes the vehicle out of service (`unsafe_on_fail`), and any `instructions`. A `number` field's `meta` may name its `unit` and the `role` it plays, such as `odometer`. + + The first cut's flat `items` checklist is still answered, read-only, for forms that have not been rebuilt; a form built from fields answers both, and `grouped_fields` is the one to render. + + A form that is not published — a draft, or one that has been archived — answers 404, the same as one that does not exist: to a driver, a form that cannot be filled in is not there. +url: "{{base_url}}/{{namespace}}/inspection-forms/:id" +method: GET +pathVariables: + - key: id + value: "{{inspection_form_id}}" + description: (Required) The inspection form to retrieve. + +scripts: + - type: afterResponse + code: |- + // Submit an Inspection answers this form, so the fields it will answer + // are chained from here by type. A form that has not been rebuilt from + // the first cut carries no fields; the ids are cleared rather than left + // pointing at a previous run's form, and the submit falls back to the + // flat `item_results` body on its own. + pm.test("Retrieve an Inspection Form answers a published form", function () { + pm.expect(pm.response.code, "expected a 2xx").to.be.oneOf([200, 201]); + }); + + const form = pm.response.json(); + const groups = (form && Array.isArray(form.grouped_fields)) ? form.grouped_fields : []; + const fields = groups.reduce((all, group) => all.concat(Array.isArray(group.fields) ? group.fields : []), []); + + const firstOfType = (type) => { + const field = fields.find((candidate) => candidate && candidate.type === type); + return field && field.id ? field.id : null; + }; + + const chain = { + inspection_pass_fail_field_id: firstOfType("pass-fail"), + inspection_number_field_id: firstOfType("number"), + inspection_signature_field_id: firstOfType("signature"), + }; + + Object.keys(chain).forEach((key) => { + if (chain[key]) { + pm.environment.set(key, chain[key]); + } else { + pm.environment.unset(key); + } + }); + + pm.test("A form built from fields answers grouped_fields", function () { + if (!groups.length) { + pm.expect(form, "a first-cut form still answers its checklist").to.have.property("items"); + return; + } + + pm.expect(fields.length, "a group with no fields in it").to.be.above(0); + fields.forEach((field) => { + pm.expect(field.id, "a field with no id cannot be answered").to.be.a("string").and.not.empty; + pm.expect(field.type, "a field with no type cannot be rendered").to.be.a("string").and.not.empty; + }); + }); + language: text/javascript + +order: 2000 diff --git a/postman/collections/Fleetbase API/Inspections/Retrieve an Inspection.request.yaml b/postman/collections/Fleetbase API/Inspections/Retrieve an Inspection.request.yaml new file mode 100644 index 0000000..971e9d1 --- /dev/null +++ b/postman/collections/Fleetbase API/Inspections/Retrieve an Inspection.request.yaml @@ -0,0 +1,13 @@ +$kind: http-request +name: "Retrieve an Inspection" +description: |- + Retrieves one inspection with everything it produced: `custom_field_values` — the driver's answers, each beside the field's `name`, `label` and `type`, with every photo and signature resolved to `{ id, url, filename, content_type }` — the `item_results` derived from the `pass-fail` answers, the `files` filed with it, and the `issue` and `work_order` raised from any failed items. +url: "{{base_url}}/{{namespace}}/inspections/:id" +method: GET +pathVariables: + - key: id + value: "{{inspection_id}}" + description: (Required) The inspection to retrieve. + + +order: 5000 diff --git a/postman/collections/Fleetbase API/Inspections/Submit an Inspection.params.yaml b/postman/collections/Fleetbase API/Inspections/Submit an Inspection.params.yaml new file mode 100644 index 0000000..b64b7e5 --- /dev/null +++ b/postman/collections/Fleetbase API/Inspections/Submit an Inspection.params.yaml @@ -0,0 +1,69 @@ +$kind: params +fields: + - name: inspection_form + type: string + required: true + description: ID of the published inspection form being filed against. + - name: driver + type: string + required: true + description: ID of the driver filing the inspection. + - name: vehicle + type: string + required: false + description: ID of the vehicle inspected. Defaults to the vehicle the driver is assigned to. + - name: started_at + type: timestamp + required: false + description: When the driver began the inspection. Defaults to the time it is filed. + - name: odometer + type: integer + required: false + description: Odometer reading at the time of the inspection. + - name: engine_hours + type: integer + required: false + description: Engine hours at the time of the inspection. + - name: item_results + type: array of objects + required: true + description: One entry per checklist item answered. At least one is required. + fields: + - name: item_key + type: string + description: The form item's key. + - name: label + type: string + required: true + description: The item as shown to the driver. + - name: category + type: string + description: The item's category on the form. + - name: passed + type: boolean + required: true + description: Whether the item passed. + - name: status + type: string + description: "`passed` or `failed`. Derived from `passed` when omitted." + - name: severity + type: string + description: "Severity of a failure — `low`, `medium`, `high` or `critical`. Sets the priority of the issue and work order raised." + - name: comments + type: string + description: The driver's note on the item. + - name: photos + type: array + description: Photos of the item, each a base64 encoded image or a URL. + - name: location + type: object + required: false + description: Where the inspection was filed from, as `latitude` and `longitude`. + - name: signature + type: object + required: false + description: The driver's signature, as the app captured it. + - name: attachments + type: array + required: false + description: Additional files attached to the inspection. diff --git a/postman/collections/Fleetbase API/Inspections/Submit an Inspection.request.yaml b/postman/collections/Fleetbase API/Inspections/Submit an Inspection.request.yaml new file mode 100644 index 0000000..ed1fa08 --- /dev/null +++ b/postman/collections/Fleetbase API/Inspections/Submit an Inspection.request.yaml @@ -0,0 +1,146 @@ +$kind: http-request +name: "Submit an Inspection" +description: |- + Files an inspection against a published form, as the driver app does at the end of a DVIR. + + A form built from typed fields is answered with `custom_field_values`: one entry per field the driver answered, naming the field by the `id` the form read gave it, with a `value_type` and a `value`. A `pass-fail` answer is an object — `passed`, `not_applicable`, `severity`, `comments`, `photos` and `unsafe`; a meter is a number, a signature or a photo is base64. The server stores every photo and signature as a file and answers with `file:` references resolved, and mirrors each `pass-fail` answer into an `item_results` row, which is what issues, work orders and the vehicle's history are built from. + + The first cut's flat `item_results` body is still accepted, for the tokenised public link and for older app builds. When both arrive the field values win and the duplicated results are ignored — which is exactly what the app sends, so one body works against either cut. + + A failed `pass-fail` answer must carry whatever its field insists on: a form that sets `require_comment_on_fail` or `require_photo_on_fail` refuses the whole submission with a 422 rather than filing half an inspection. + + Without `vehicle` the inspection is recorded against the vehicle the driver is currently assigned to. Without `started_at` the inspection is taken to have started when it was filed. + + The app queues a submit while offline and replays it later. Send an `Idempotency-Key` header with each attempt: a replay with a key already used by the same driver answers with the inspection the first attempt filed rather than filing a second one. + + The response is the inspection with its `custom_field_values`, `item_results` and `files`, and the `issue` and `work_order` the form's settings raised from any failed items. +url: "{{base_url}}/{{namespace}}/inspections" +method: POST +headers: + Idempotency-Key: "{{$guid}}" +body: + type: json + content: |- + { + "inspection_form": "{{inspection_form_id}}", + "driver": "{{driver_id}}", + "vehicle": "{{vehicle_id}}", + "started_at": "2026-09-09T07:10:00Z", + "odometer": 120400, + "engine_hours": 3100, + "location": { + "latitude": 1.3521, + "longitude": 103.8198 + }, + "custom_field_values": [], + "item_results": [ + { + "item_key": "brakes", + "label": "Brakes", + "category": "Safety", + "passed": false, + "severity": "critical", + "comments": "Pedal is soft.", + "photos": ["{{proof_photo_base64}}"] + }, + { + "item_key": "lights", + "label": "Lights", + "category": "Safety", + "passed": true + } + ] + } + +scripts: + - type: beforeRequest + code: |- + // The app sends both bodies and lets the server choose; so does this. + // `custom_field_values` is filled in from the fields Retrieve an + // Inspection Form chained, because a field is named by an id that only + // exists once the form has been read. A form that has not been rebuilt + // from the first cut has no fields to name, so the entry is left empty + // and the server files the flat `item_results` instead — which is what + // an older app build sends anyway. + const resolved = (key) => { + const value = pm.environment.get(key); + return value && !/^\{\{.*\}\}$/.test(String(value)) ? String(value) : null; + }; + + const passFail = resolved("inspection_pass_fail_field_id"); + const meter = resolved("inspection_number_field_id"); + const signature = resolved("inspection_signature_field_id"); + const photo = resolved("proof_photo_base64"); + const values = []; + + if (passFail) { + values.push({ + custom_field: passFail, + value_type: "object", + value: { + passed: false, + not_applicable: false, + severity: "critical", + comments: "Pedal is soft.", + photos: photo ? [photo] : [], + unsafe: true, + }, + }); + } + + if (meter) { + values.push({ custom_field: meter, value_type: "number", value: 120400 }); + } + + if (signature && photo) { + values.push({ custom_field: signature, value_type: "file", value: photo }); + } + + const body = JSON.parse(pm.request.body.raw); + body.custom_field_values = values; + pm.request.body.update({ mode: "raw", raw: JSON.stringify(body, null, 2) }); + + pm.environment.set("inspection_submitted_field_values", String(values.length)); + language: text/javascript + - type: afterResponse + code: |- + const json_response = pm.response.json(); + + if (json_response && json_response.id) { + pm.environment.set("inspection_id", json_response.id); + } + + pm.test("Submit an Inspection files the report", function () { + pm.expect(pm.response.code, "expected a 2xx").to.be.oneOf([200, 201]); + pm.expect(json_response, "no inspection returned").to.be.an("object"); + pm.expect(json_response.id, "the filed inspection has no id").to.be.a("string").and.not.empty; + pm.expect(json_response.item_results, "no item results were derived").to.be.an("array"); + }); + + pm.test("Answers filed as field values come back with their files resolved", function () { + const sent = Number(pm.environment.get("inspection_submitted_field_values") || 0); + + if (!sent) { + pm.expect(json_response.item_results.length, "a flat submit files its results").to.be.above(0); + return; + } + + pm.expect(json_response.custom_field_values, "no field values were answered").to.be.an("array"); + pm.expect(json_response.custom_field_values.length, "fewer values than were sent").to.eql(sent); + + json_response.custom_field_values.forEach((value) => { + pm.expect(value.custom_field, "a value that names no field").to.be.a("string").and.not.empty; + + // A photo or signature is stored as a file and answered as one, + // never handed back as the base64 that was sent. + const photos = (value.value && Array.isArray(value.value.photos)) ? value.value.photos : []; + photos.forEach((file) => pm.expect(file, "a photo left unresolved").to.be.an("object").and.to.have.property("url")); + + if (value.value_type === "file" && value.value) { + pm.expect(value.value, "a signature left unresolved").to.be.an("object").and.to.have.property("url"); + } + }); + }); + language: text/javascript + +order: 3000