27becf7c68
Per MFR_RESTClient/Docs/mfr_interface_description.md (added): - MFRClientConfig: configurable TimeoutMs (default 30000), UserAgent, MaxRetries, and a derived RestRoot (/mfr) alongside the OData BaseUrl. - MFRClient: apply request Timeout + UserAgent, send Accept: application/json, and retry idempotent GETs on transient failures (HTTP 429/5xx and network/timeout) with exponential backoff + jitter, honouring Retry-After. - Added ReadODataAllPages to follow @odata.nextLink pagination. Cleanup: removed legacy VB project files (MFR_RESTClient.vbproj, .vbproj.user, app.config My.MySettings) and stopped tracking the generated MFR_RESTClient.xml (now git-ignored). The active project is MFR_RESTClient.csproj. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
1277 lines
40 KiB
Markdown
1277 lines
40 KiB
Markdown
---
|
|
title: "mfr Mobile Field Report - Interface Description for AI Coding Tools"
|
|
subtitle: "REST/OData integration contract derived from the public mfr wiki, public Postman documentation context, and public implementation evidence"
|
|
author: "Prepared for AI-assisted development"
|
|
date: "2026-06-05"
|
|
---
|
|
|
|
# mfr Mobile Field Report - Interface Description for AI Coding Tools
|
|
|
|
## 1. Purpose and scope
|
|
|
|
This document describes the public integration surface of **mfr - mobile field report** in a form that can be handed to AI coding tools, SDK generators, or developers building integrations.
|
|
|
|
The intent is not to replace the vendor's current Postman collection or tenant-specific API documentation. Instead, it provides a structured, implementation-oriented contract with:
|
|
|
|
- API surfaces and base URLs.
|
|
- Authentication assumptions.
|
|
- OData conventions.
|
|
- Domain resources and likely entity relationships.
|
|
- Endpoint catalog with confidence labels.
|
|
- Request/response examples.
|
|
- Error-handling, pagination, filtering, and retry guidance.
|
|
- TypeScript SDK design guidance.
|
|
- An OpenAPI starter definition for the confirmed REST endpoints and common OData endpoints.
|
|
- Verification checklist for turning this draft into a production-certified contract.
|
|
|
|
## 2. Source map and confidence model
|
|
|
|
### 2.1 Source URLs supplied
|
|
|
|
1. Official mfr wiki page: `https://faq.mobilefieldreport.com/de/wiki/beschreibung-schnittstelle`
|
|
2. Public Postman documentation URL: `https://documenter.getpostman.com/view/6932380/2sB3dWsn6U`
|
|
|
|
### 2.2 Additional public evidence used
|
|
|
|
The following public evidence was used to fill in implementation details where the vendor wiki only describes the high-level interface family:
|
|
|
|
- Public Postman/OData indexing snippets for older mfr OData documentation.
|
|
- Public n8n community discussion and public node implementation references for mfr document upload.
|
|
- Public node implementation evidence for OData endpoints such as Companies, Contacts, Appointments, ItemTypes, and ServiceObjects.
|
|
- OData standard documentation for query semantics.
|
|
- Ecosystem references such as Zapier connector fields and public integration guides.
|
|
|
|
### 2.3 Confidence labels
|
|
|
|
Use the following labels whenever implementing or generating code from this document.
|
|
|
|
| Label | Meaning | How to use it |
|
|
|---|---|---|
|
|
| Confirmed | Directly visible in official mfr wiki or strongly supported by official-looking endpoint examples. | Safe to implement first, still validate against tenant credentials. |
|
|
| Strongly indicated | Supported by public implementation evidence or older public Postman/OData documentation. | Implement behind integration tests; verify against current Postman collection. |
|
|
| Inferred | Derived from OData standards or naming conventions. | Treat as a default strategy, not a contract guarantee. |
|
|
| Unknown / verify | Not available from public text extraction or likely tenant-specific. | Do not hard-code; ask tenant/vendor or inspect live metadata. |
|
|
|
|
## 3. Interface families
|
|
|
|
The official wiki identifies these integration mechanisms:
|
|
|
|
| Interface | Description | Typical use |
|
|
|---|---|---|
|
|
| UGL Artikel Import | Import of item/article data. | Bulk item/product import, likely from ERP/catalog systems. |
|
|
| OData | Microsoft-origin REST data-exchange standard; successor-style integration surface compared with older SOAP-style integrations. | CRUD and querying over business entities. |
|
|
| REST API | HTTP operations via POST, GET, PUT, and DELETE for data and documents. | Special operations such as deep creation and document upload. |
|
|
| Navision / Microsoft Dynamics development kit | Integration kit for Dynamics/Navision ecosystems. | ERP connection and synchronization. |
|
|
| AMQP Message Bus | Change notifications. | Event-driven synchronization, cache invalidation, async workflows. |
|
|
|
|
The rest of this document focuses on the HTTP APIs: **OData** and **REST API**.
|
|
|
|
## 4. Base URLs and environments
|
|
|
|
### 4.1 Observed base host
|
|
|
|
The public examples consistently use:
|
|
|
|
```text
|
|
https://portal.mobilefieldreport.com
|
|
```
|
|
|
|
### 4.2 Resource roots
|
|
|
|
| Root | Confidence | Meaning |
|
|
|---|---:|---|
|
|
| `https://portal.mobilefieldreport.com/odata` | Strongly indicated | OData resource root for entity CRUD and queries. |
|
|
| `https://portal.mobilefieldreport.com/mfr` | Confirmed | REST operation root for mfr-specific operations. |
|
|
|
|
### 4.3 Recommended SDK configuration
|
|
|
|
Do not hard-code the host. Provide configuration:
|
|
|
|
```ts
|
|
export interface MfrClientConfig {
|
|
baseUrl?: string; // default: https://portal.mobilefieldreport.com
|
|
odataPath?: string; // default: /odata
|
|
restPath?: string; // default: /mfr
|
|
username: string;
|
|
password: string;
|
|
timeoutMs?: number; // default: 30000
|
|
userAgent?: string;
|
|
}
|
|
```
|
|
|
|
## 5. Authentication and headers
|
|
|
|
### 5.1 Authentication
|
|
|
|
Public implementation evidence indicates **HTTP Basic Authentication** using a username and password. Confirm against the tenant's current Postman collection before production release.
|
|
|
|
Recommended client behavior:
|
|
|
|
- Use HTTPS only.
|
|
- Store credentials in a secrets manager or environment variables.
|
|
- Never log the raw `Authorization` header.
|
|
- Support tenant-specific credentials.
|
|
- Fail fast if credentials are missing.
|
|
|
|
Example environment variables:
|
|
|
|
```bash
|
|
MFR_BASE_URL="https://portal.mobilefieldreport.com"
|
|
MFR_USERNAME="service-account@example.com"
|
|
MFR_PASSWORD="***"
|
|
```
|
|
|
|
### 5.2 Default headers
|
|
|
|
For JSON operations:
|
|
|
|
```http
|
|
Accept: application/json
|
|
Content-Type: application/json
|
|
Authorization: Basic <base64(username:password)>
|
|
```
|
|
|
|
For file upload:
|
|
|
|
```http
|
|
Accept: application/json
|
|
Content-Type: multipart/form-data; boundary=<generated>
|
|
Authorization: Basic <base64(username:password)>
|
|
```
|
|
|
|
Let the HTTP client generate the `multipart/form-data` boundary.
|
|
|
|
## 6. OData conventions
|
|
|
|
### 6.1 Entity paths and IDs
|
|
|
|
Observed OData entity URLs use the format:
|
|
|
|
```text
|
|
/odata/ServiceObjects(9875849220L)
|
|
/odata/Companies(1234567890L)
|
|
```
|
|
|
|
Implementation guidance:
|
|
|
|
- Treat entity IDs as **64-bit numeric identifiers**.
|
|
- Use `string` or `bigint` in JavaScript/TypeScript to avoid precision loss.
|
|
- Public examples append an `L` suffix to path IDs. Preserve this suffix when constructing OData entity URLs unless live metadata proves otherwise.
|
|
- Some list operations support filtering by `ExternalId`.
|
|
|
|
Recommended TypeScript ID type:
|
|
|
|
```ts
|
|
export type MfrId = string; // store as decimal string; append L only in URL path builder
|
|
```
|
|
|
|
### 6.2 OData query parameters
|
|
|
|
Use standard OData query options where supported:
|
|
|
|
| Option | Use |
|
|
|---|---|
|
|
| `$filter` | Server-side filtering. |
|
|
| `$select` | Reduce response fields. |
|
|
| `$expand` | Load related entities, e.g. `Contacts`. |
|
|
| `$orderby` | Sort results. |
|
|
| `$top` | Limit page size. |
|
|
| `$skip` | Offset pagination. |
|
|
| `$count` | Ask for total count if the service supports it. |
|
|
|
|
### 6.3 Filtering examples
|
|
|
|
```text
|
|
/odata/Companies?$filter=ExternalId eq 'CUST-10001'
|
|
/odata/ServiceObjects?$filter=ExternalId eq 'SO-20002'
|
|
/odata/Appointments?$filter=StartDateTime ge datetime'2026-06-01T00:00:00Z'
|
|
/odata/Contacts?$filter=CompanyId eq 1234567890L&$expand=Company
|
|
```
|
|
|
|
Implementation guidance:
|
|
|
|
- URL-encode query parameters.
|
|
- Escape single quotes in OData string literals by doubling them: `O'Brien` -> `'O''Brien'`.
|
|
- Treat datetime literal syntax as implementation-specific until verified. Public integration guidance indicates `datetime'YYYY-MM-DDTHH:mm:ssZ'`.
|
|
- Prefer ISO 8601 UTC timestamps in generated code.
|
|
|
|
### 6.4 Pagination
|
|
|
|
The public OData implementation evidence uses `$top` and `$skip`.
|
|
|
|
Recommended default pagination strategy:
|
|
|
|
```ts
|
|
const pageSize = 100;
|
|
for (let skip = 0; ; skip += pageSize) {
|
|
const page = await client.listCompanies({ top: pageSize, skip });
|
|
if (page.length === 0) break;
|
|
yield* page;
|
|
if (page.length < pageSize) break;
|
|
}
|
|
```
|
|
|
|
Avoid assuming server-side continuation tokens unless discovered in live responses.
|
|
|
|
### 6.5 Updating entities
|
|
|
|
For OData updates, verify the supported method in the current tenant. Common OData patterns are:
|
|
|
|
- `POST /odata/EntitySet` to create.
|
|
- `GET /odata/EntitySet(idL)` to read.
|
|
- `PUT /odata/EntitySet(idL)` or `PATCH /odata/EntitySet(idL)` to update.
|
|
- `DELETE /odata/EntitySet(idL)` to delete.
|
|
|
|
If the Postman collection exposes only some verbs, implement only the exposed verbs.
|
|
|
|
## 7. Domain model overview
|
|
|
|
The core mfr domain is field-service execution: service requests/jobs, service objects/locations, customers/companies, appointments, technicians/users, contacts, item types/products, documents, checklists, reports, and billing/time data.
|
|
|
|
### 7.1 Core entities
|
|
|
|
| Entity | Likely OData set | Description | Confidence |
|
|
|---|---|---|---:|
|
|
| Company / Customer | `Companies` | Customer organization or physical person. | Strongly indicated |
|
|
| Contact | `Contacts` | Contact person with telephone, mobile, and email. | Strongly indicated |
|
|
| Service Request / Job | `ServiceRequests` | Work order/job/task container. | Confirmed / strongly indicated |
|
|
| Service Object / Location | `ServiceObjects` | Installed object, location, asset, or service site. | Confirmed / strongly indicated |
|
|
| Appointment | `Appointments` | Scheduled visit/time slot, optionally linked to request, service object, contact, technician. | Strongly indicated |
|
|
| Item Type / Product | `ItemTypes` | Product, article, service item, stock/material type. | Strongly indicated |
|
|
| Document | REST document operations | File upload and association. | Strongly indicated |
|
|
| User / Technician | `Users` or equivalent | Technician / mfr user account. | Strongly indicated |
|
|
| Tags | unknown | Labels attached to jobs/entities. | Strongly indicated by ecosystem connectors |
|
|
| Reports | REST report operation | Generate report by job and report definition code. | Strongly indicated by ecosystem connectors |
|
|
|
|
### 7.2 Customer / Company model
|
|
|
|
Observed and inferred fields:
|
|
|
|
```ts
|
|
export interface MfrCompany {
|
|
Id?: MfrId;
|
|
Name: string;
|
|
ExternalId?: string;
|
|
IsPhysicalPerson?: boolean | 0 | 1;
|
|
Location?: MfrLocation;
|
|
MainContact?: MfrContact;
|
|
SupportTelephone?: string;
|
|
SupportFax?: string;
|
|
SupportMail?: string;
|
|
Note?: string;
|
|
}
|
|
```
|
|
|
|
### 7.3 Location model
|
|
|
|
```ts
|
|
export interface MfrLocation {
|
|
Id?: MfrId;
|
|
AddressString?: string;
|
|
Postal?: string;
|
|
City?: string;
|
|
Country?: string; // e.g. DE
|
|
Latitude?: number;
|
|
Longitude?: number;
|
|
}
|
|
```
|
|
|
|
### 7.4 Contact model
|
|
|
|
```ts
|
|
export interface MfrContact {
|
|
Id?: MfrId;
|
|
FirstName?: string;
|
|
LastName?: string;
|
|
Telephone?: string;
|
|
MobilePhone?: string;
|
|
Email?: string;
|
|
CompanyId?: MfrId;
|
|
}
|
|
```
|
|
|
|
### 7.5 Service object model
|
|
|
|
```ts
|
|
export interface MfrServiceObject {
|
|
Id?: MfrId;
|
|
Name: string;
|
|
ExternalId?: string;
|
|
CompanyId?: MfrId;
|
|
Location?: MfrLocation;
|
|
Contacts?: MfrContact[];
|
|
Country?: string;
|
|
CreateGeoLocation?: boolean;
|
|
CreateFromServiceRequestTemplateId?: MfrId;
|
|
}
|
|
```
|
|
|
|
### 7.6 Service request / job model
|
|
|
|
```ts
|
|
export type ServiceRequestState =
|
|
| 'ReadyForScheduling'
|
|
| 'Draft'
|
|
| 'Scheduled'
|
|
| 'InProgress'
|
|
| 'Completed'
|
|
| 'Cancelled'
|
|
| string; // keep extensible because tenant values may vary
|
|
|
|
export interface MfrServiceRequest {
|
|
Id?: MfrId;
|
|
Name: string;
|
|
Description?: string;
|
|
ExternalId?: string;
|
|
CustomerId?: MfrId;
|
|
Customer?: MfrCompany;
|
|
State?: ServiceRequestState;
|
|
ServiceObjects?: MfrServiceObject[];
|
|
Appointments?: MfrAppointment[];
|
|
}
|
|
```
|
|
|
|
### 7.7 Appointment model
|
|
|
|
```ts
|
|
export interface MfrAppointment {
|
|
Id?: MfrId;
|
|
ContactId?: MfrId;
|
|
ServiceRequestId?: MfrId;
|
|
ServiceObjectId?: MfrId;
|
|
StartDateTime: string; // ISO 8601
|
|
EndDateTime: string; // ISO 8601
|
|
Type?: string;
|
|
Description?: string;
|
|
TechnicianUsername?: string;
|
|
}
|
|
```
|
|
|
|
### 7.8 Item type / product model
|
|
|
|
```ts
|
|
export interface MfrItemType {
|
|
Id?: MfrId;
|
|
NameOrNumber: string;
|
|
ExternalId?: string;
|
|
UnitId?: MfrId;
|
|
Type?: string;
|
|
Costs?: number;
|
|
Price?: number;
|
|
Manufacture?: string;
|
|
VAT?: number;
|
|
Description?: string;
|
|
GlobalTradeItemNr?: string;
|
|
}
|
|
```
|
|
|
|
## 8. Confirmed REST operation: deep create service request
|
|
|
|
### 8.1 Endpoint
|
|
|
|
```http
|
|
POST /mfr/ServiceRequest/Deep
|
|
Host: portal.mobilefieldreport.com
|
|
Content-Type: application/json
|
|
Accept: application/json
|
|
```
|
|
|
|
Full observed URL:
|
|
|
|
```text
|
|
https://portal.mobilefieldreport.com/mfr/ServiceRequest/Deep
|
|
```
|
|
|
|
### 8.2 Purpose
|
|
|
|
Create a service request/job together with nested customer and service-object data in one operation.
|
|
|
|
### 8.3 Important field behavior
|
|
|
|
`CreateFromServiceRequestTemplateId` determines which order/service-request template is used when creating the order. The template ID is found in mfr administration under templates (`Verwaltung > Vorlagen`) according to the official wiki example.
|
|
|
|
### 8.4 Request body example
|
|
|
|
```json
|
|
{
|
|
"Name": "Auftragsbezeichnung",
|
|
"Description": "Auftragsbeschreibung",
|
|
"Customer": {
|
|
"Id": 0,
|
|
"IsPhysicalPerson": 1,
|
|
"ExternalId": "543",
|
|
"Name": "Frank Service GmbH",
|
|
"Location": {
|
|
"Postal": "23423",
|
|
"AddressString": "Dorfstrasse 3",
|
|
"City": "Leipzig"
|
|
}
|
|
},
|
|
"State": "ReadyForScheduling",
|
|
"ServiceObjects": [
|
|
{
|
|
"Id": 0,
|
|
"CreateFromServiceRequestTemplateId": 2342342,
|
|
"CreateGeoLocation": true,
|
|
"Country": "DE",
|
|
"Contacts": [
|
|
{
|
|
"FirstName": "Frank",
|
|
"LastName": "Peterson",
|
|
"Telephone": "023423",
|
|
"MobilePhone": "234234",
|
|
"Email": "test@test.de"
|
|
}
|
|
],
|
|
"Name": "Service object name",
|
|
"ExternalId": "28",
|
|
"Location": {
|
|
"AddressString": "Gleisstrasse 2",
|
|
"Postal": "04229",
|
|
"City": "Leipzig"
|
|
}
|
|
}
|
|
]
|
|
}
|
|
```
|
|
|
|
### 8.5 Response body
|
|
|
|
The exact response shape must be verified from the current Postman collection or live tenant. A robust client should support these common response patterns:
|
|
|
|
```ts
|
|
export type DeepCreateServiceRequestResponse =
|
|
| MfrServiceRequest
|
|
| { Id: MfrId; ServiceRequestId?: MfrId; [key: string]: unknown }
|
|
| { value: MfrServiceRequest };
|
|
```
|
|
|
|
### 8.6 Validation rules for generated clients
|
|
|
|
- Require `Name`.
|
|
- Require either a nested `Customer` or a known `CustomerId` if tenant supports it.
|
|
- Require at least one service object when using the nested official example flow.
|
|
- Validate email format where provided, but do not reject missing contact fields unless the tenant requires them.
|
|
- Use integer/decimal-string template IDs, not floating point.
|
|
- Support German and international address formats.
|
|
|
|
## 9. Strongly indicated REST operation: document upload and create
|
|
|
|
### 9.1 Endpoint
|
|
|
|
```http
|
|
POST /mfr/Document/UploadAndCreate
|
|
Host: portal.mobilefieldreport.com
|
|
Content-Type: multipart/form-data
|
|
Accept: application/json
|
|
```
|
|
|
|
Full observed URL:
|
|
|
|
```text
|
|
https://portal.mobilefieldreport.com/mfr/Document/UploadAndCreate
|
|
```
|
|
|
|
### 9.2 Purpose
|
|
|
|
Upload a document file and create an mfr document record, usually to associate the document with a job, service request, service object, or another entity.
|
|
|
|
### 9.3 Multipart fields
|
|
|
|
Public implementation evidence shows a multipart request containing:
|
|
|
|
| Field | Type | Required | Notes |
|
|
|---|---|---:|---|
|
|
| `file` | binary file | yes | The file content, with filename and content type. |
|
|
| `options` | JSON string | likely yes | Public implementation evidence includes `{ "filename": "..." }`. Tenant/Postman may define additional association fields. |
|
|
|
|
### 9.4 Example multipart pseudo-code
|
|
|
|
```ts
|
|
const form = new FormData();
|
|
form.append('file', fileBuffer, {
|
|
filename: 'report.pdf',
|
|
contentType: 'application/pdf'
|
|
});
|
|
form.append('options', JSON.stringify({
|
|
filename: 'report.pdf'
|
|
// Verify exact association fields in current Postman docs:
|
|
// serviceRequestId, jobId, entityId, entityType, description, etc.
|
|
}));
|
|
|
|
await http.post('/mfr/Document/UploadAndCreate', form, {
|
|
headers: form.getHeaders()
|
|
});
|
|
```
|
|
|
|
### 9.5 Client design guidance
|
|
|
|
Because the public text does not expose the complete `options` schema, generate an extensible type:
|
|
|
|
```ts
|
|
export interface UploadDocumentOptions {
|
|
filename?: string;
|
|
serviceRequestId?: MfrId;
|
|
jobId?: MfrId;
|
|
serviceObjectId?: MfrId;
|
|
entityId?: MfrId;
|
|
entityType?: string;
|
|
description?: string;
|
|
[key: string]: unknown;
|
|
}
|
|
```
|
|
|
|
Then expose:
|
|
|
|
```ts
|
|
uploadDocument(file: Blob | Buffer | Readable, options: UploadDocumentOptions): Promise<MfrDocument>
|
|
```
|
|
|
|
## 10. OData endpoint catalog
|
|
|
|
This catalog lists endpoints that are confirmed or strongly indicated by public evidence. Verify all mutable operations against the current Postman collection before production use.
|
|
|
|
### 10.1 Companies
|
|
|
|
**Entity set:** `Companies`
|
|
**Purpose:** customer/company master data.
|
|
|
|
```http
|
|
GET /odata/Companies?$top=100&$skip=0&$filter=ExternalId eq 'CUST-001'
|
|
GET /odata/Companies({id}L)
|
|
POST /odata/Companies
|
|
PUT/PATCH/DELETE /odata/Companies({id}L) # verify
|
|
```
|
|
|
|
Common create/update body:
|
|
|
|
```json
|
|
{
|
|
"Name": "Example GmbH",
|
|
"ExternalId": "CUST-001",
|
|
"IsPhysicalPerson": false,
|
|
"Location": {
|
|
"AddressString": "Hauptstrasse 1",
|
|
"Postal": "10115",
|
|
"City": "Berlin",
|
|
"Country": "DE"
|
|
},
|
|
"MainContact": {
|
|
"FirstName": "Erika",
|
|
"LastName": "Mustermann",
|
|
"Email": "erika@example.com",
|
|
"Telephone": "+49 30 123456"
|
|
},
|
|
"SupportTelephone": "+49 30 55555",
|
|
"SupportMail": "support@example.com",
|
|
"Note": "Imported from ERP"
|
|
}
|
|
```
|
|
|
|
### 10.2 Contacts
|
|
|
|
**Entity set:** `Contacts`
|
|
**Purpose:** people/contact data.
|
|
|
|
```http
|
|
GET /odata/Contacts?$top=100&$skip=0&$expand=Company
|
|
GET /odata/Contacts({id}L)
|
|
POST /odata/Contacts # verify if exposed in current collection
|
|
PUT/PATCH/DELETE /odata/Contacts({id}L) # verify
|
|
```
|
|
|
|
Common fields:
|
|
|
|
```json
|
|
{
|
|
"FirstName": "Frank",
|
|
"LastName": "Peterson",
|
|
"Telephone": "023423",
|
|
"MobilePhone": "234234",
|
|
"Email": "frank.peterson@example.com",
|
|
"CompanyId": "1234567890"
|
|
}
|
|
```
|
|
|
|
### 10.3 Service objects
|
|
|
|
**Entity set:** `ServiceObjects`
|
|
**Purpose:** service sites, assets, or locations connected to customers and jobs.
|
|
|
|
```http
|
|
GET /odata/ServiceObjects?$top=100&$skip=0&$expand=Contacts
|
|
GET /odata/ServiceObjects({id}L)?$expand=Contacts
|
|
POST /odata/ServiceObjects
|
|
PUT/PATCH/DELETE /odata/ServiceObjects({id}L) # verify
|
|
```
|
|
|
|
Common create body:
|
|
|
|
```json
|
|
{
|
|
"Name": "Boiler Room A",
|
|
"ExternalId": "SO-001",
|
|
"CompanyId": "1234567890",
|
|
"Location": {
|
|
"AddressString": "Gleisstrasse 2",
|
|
"Postal": "04229",
|
|
"City": "Leipzig",
|
|
"Country": "DE"
|
|
}
|
|
}
|
|
```
|
|
|
|
### 10.4 Service requests / jobs
|
|
|
|
**Likely entity set:** `ServiceRequests`
|
|
**Purpose:** job/work-order/task data.
|
|
|
|
```http
|
|
GET /odata/ServiceRequests?$top=100&$skip=0&$filter=ExternalId eq 'JOB-001'
|
|
GET /odata/ServiceRequests({id}L)
|
|
POST /mfr/ServiceRequest/Deep
|
|
POST /odata/ServiceRequests # verify if simple create is exposed
|
|
PUT/PATCH/DELETE /odata/ServiceRequests({id}L) # verify
|
|
```
|
|
|
|
Use the deep REST endpoint when nested creation is required.
|
|
|
|
### 10.5 Appointments
|
|
|
|
**Entity set:** `Appointments`
|
|
**Purpose:** scheduled service visits.
|
|
|
|
```http
|
|
GET /odata/Appointments?$top=100&$skip=0
|
|
GET /odata/Appointments({id}L)
|
|
POST /odata/Appointments
|
|
PUT/PATCH/DELETE /odata/Appointments({id}L) # verify
|
|
```
|
|
|
|
Common create body:
|
|
|
|
```json
|
|
{
|
|
"ContactId": "1234567890",
|
|
"StartDateTime": "2026-06-10T08:00:00Z",
|
|
"EndDateTime": "2026-06-10T10:00:00Z",
|
|
"Type": "Service"
|
|
}
|
|
```
|
|
|
|
Ecosystem connector evidence indicates appointment creation may also require appointment type, duration, start date/time, end date/time, description, and technician username in some flows. Treat technician assignment fields as tenant-specific until verified.
|
|
|
|
### 10.6 Item types / products
|
|
|
|
**Entity set:** `ItemTypes`
|
|
**Purpose:** product/article/service item master data.
|
|
|
|
```http
|
|
GET /odata/ItemTypes?$top=100&$skip=0&$filter=ExternalId eq 'ITEM-001'
|
|
GET /odata/ItemTypes({id}L)
|
|
POST /odata/ItemTypes
|
|
PUT/PATCH/DELETE /odata/ItemTypes({id}L) # verify
|
|
```
|
|
|
|
Common create body:
|
|
|
|
```json
|
|
{
|
|
"NameOrNumber": "PUMP-001",
|
|
"ExternalId": "ERP-ITEM-001",
|
|
"UnitId": "1",
|
|
"Type": "Material",
|
|
"Costs": 12.50,
|
|
"Price": 19.95,
|
|
"Manufacture": "Example Manufacturer",
|
|
"VAT": 19,
|
|
"Description": "Replacement pump",
|
|
"GlobalTradeItemNr": "4000000000000"
|
|
}
|
|
```
|
|
|
|
### 10.7 Entity links / relationships
|
|
|
|
Older public Postman/OData snippets show relationship operations like:
|
|
|
|
```http
|
|
POST /odata/ServiceObjects({serviceObjectId}L)/$links/Contacts
|
|
```
|
|
|
|
The exact request body for link creation must be verified. Common OData link bodies use a URI reference pattern such as:
|
|
|
|
```json
|
|
{
|
|
"uri": "https://portal.mobilefieldreport.com/odata/Contacts(1234567890L)"
|
|
}
|
|
```
|
|
|
|
Do not implement link writes without validating the body shape against live Postman documentation.
|
|
|
|
## 11. Common workflows
|
|
|
|
### 11.1 Create a new customer and service request in one call
|
|
|
|
Preferred when using the official deep-create flow:
|
|
|
|
1. Build nested customer object.
|
|
2. Build at least one service object with location and contact.
|
|
3. Add `CreateFromServiceRequestTemplateId` if a template should be applied.
|
|
4. Set `State` to an accepted state such as `ReadyForScheduling`.
|
|
5. `POST /mfr/ServiceRequest/Deep`.
|
|
6. Store returned mfr IDs and external IDs in the source system.
|
|
|
|
### 11.2 Synchronize companies from ERP
|
|
|
|
1. For each ERP customer, query by `ExternalId`:
|
|
`GET /odata/Companies?$filter=ExternalId eq 'ERP-123'`.
|
|
2. If found, update the existing company if update is supported.
|
|
3. If not found, create via `POST /odata/Companies`.
|
|
4. Store the returned mfr `Id` as a cross-reference.
|
|
5. Use rate limiting and retry on transient 5xx/429 errors.
|
|
|
|
### 11.3 Create service object under an existing customer
|
|
|
|
1. Resolve customer/company by external ID.
|
|
2. `POST /odata/ServiceObjects` with `CompanyId` and `Location`.
|
|
3. Optionally attach contacts through nested data or OData link operations if supported.
|
|
4. Query with `$expand=Contacts` to verify relationships.
|
|
|
|
### 11.4 Create appointment for an existing job
|
|
|
|
1. Resolve service request/job ID.
|
|
2. Resolve technician/user if required by tenant.
|
|
3. Resolve appointment type if tenant requires enumerated types.
|
|
4. `POST /odata/Appointments` with start/end time and links.
|
|
5. Read back the appointment and verify assigned technician/status.
|
|
|
|
### 11.5 Upload document for a job
|
|
|
|
1. Read file from source system.
|
|
2. Build multipart form with `file` and `options`.
|
|
3. Include `filename` and association fields required by tenant/Postman collection.
|
|
4. `POST /mfr/Document/UploadAndCreate`.
|
|
5. Store returned document ID.
|
|
6. Read job/document relationship if an endpoint is available.
|
|
|
|
## 12. Error handling and retries
|
|
|
|
### 12.1 Expected HTTP status classes
|
|
|
|
| Status | Meaning | Client behavior |
|
|
|---:|---|---|
|
|
| 200 | Success, often for reads or operations returning an object. | Parse JSON. |
|
|
| 201 | Created. | Parse JSON and capture created ID. |
|
|
| 204 | Success without body. | Return void/success. |
|
|
| 400 | Invalid payload or query. | Do not retry; surface validation details. |
|
|
| 401 | Missing/invalid credentials. | Do not retry blindly; refresh/reconfigure credentials. |
|
|
| 403 | Not authorized for tenant/resource. | Do not retry; escalate permissions. |
|
|
| 404 | Resource not found. | Return typed not-found error. |
|
|
| 409 | Conflict/duplicate/concurrent update. | Consider idempotency lookup by `ExternalId`. |
|
|
| 413 | Upload too large. | Surface file size limit. |
|
|
| 415 | Unsupported media type. | Check `Content-Type` and file MIME type. |
|
|
| 429 | Rate limited. | Retry with backoff if allowed. |
|
|
| 500-599 | Server/transient errors. | Retry idempotent operations with backoff. |
|
|
|
|
### 12.2 Error parser
|
|
|
|
Generated clients should not assume a single error body shape. Implement a permissive parser:
|
|
|
|
```ts
|
|
export interface MfrApiErrorBody {
|
|
error?: unknown;
|
|
message?: string;
|
|
Message?: string;
|
|
details?: unknown;
|
|
[key: string]: unknown;
|
|
}
|
|
```
|
|
|
|
### 12.3 Retry policy
|
|
|
|
Recommended default:
|
|
|
|
- Retry only GET and explicitly idempotent operations by default.
|
|
- Retry 429 and 5xx.
|
|
- Use exponential backoff with jitter.
|
|
- Never retry file uploads automatically unless the caller opts in.
|
|
- Before retrying create operations, search by `ExternalId` to avoid duplicates.
|
|
|
|
## 13. Idempotency and external IDs
|
|
|
|
Many integration flows include `ExternalId`. Treat `ExternalId` as the source-system cross-reference.
|
|
|
|
Recommended create-or-update pattern:
|
|
|
|
```ts
|
|
async function upsertCompany(input: MfrCompany): Promise<MfrCompany> {
|
|
if (!input.ExternalId) throw new Error('ExternalId is required for idempotent upsert');
|
|
const existing = await client.findCompanyByExternalId(input.ExternalId);
|
|
if (existing) return client.updateCompany(existing.Id!, input);
|
|
return client.createCompany(input);
|
|
}
|
|
```
|
|
|
|
If update is not available or not verified, implement create-only plus duplicate detection.
|
|
|
|
## 14. Time zones and date fields
|
|
|
|
Use UTC ISO 8601 strings for API payloads unless tenant docs specify local time. Store the user's/local timezone separately if required for scheduling UX.
|
|
|
|
Recommended conversion:
|
|
|
|
```ts
|
|
const startUtc = zonedTimeToUtc('2026-06-10 08:00', 'Europe/Berlin').toISOString();
|
|
```
|
|
|
|
Payload example:
|
|
|
|
```json
|
|
{
|
|
"StartDateTime": "2026-06-10T06:00:00Z",
|
|
"EndDateTime": "2026-06-10T08:00:00Z"
|
|
}
|
|
```
|
|
|
|
## 15. Security and privacy requirements
|
|
|
|
Generated integrations should follow these requirements:
|
|
|
|
- Use HTTPS only.
|
|
- Keep API credentials out of code and logs.
|
|
- Redact `Authorization`, password fields, file contents, and personal data in logs.
|
|
- Minimize `$expand` and `$select` to only needed fields.
|
|
- Avoid exporting unnecessary personal data from contacts and appointments.
|
|
- Add an audit trail for create/update/delete operations.
|
|
- Use service accounts with least privilege when possible.
|
|
- Treat uploaded files as potentially sensitive.
|
|
|
|
## 16. TypeScript SDK blueprint
|
|
|
|
### 16.1 Client class
|
|
|
|
```ts
|
|
export class MfrClient {
|
|
constructor(private config: MfrClientConfig) {}
|
|
|
|
// REST operations
|
|
createServiceRequestDeep(input: DeepCreateServiceRequestRequest): Promise<DeepCreateServiceRequestResponse>;
|
|
uploadDocument(file: Uploadable, options: UploadDocumentOptions): Promise<MfrDocument>;
|
|
|
|
// Companies
|
|
listCompanies(query?: ODataQuery): Promise<MfrCompany[]>;
|
|
getCompany(id: MfrId): Promise<MfrCompany>;
|
|
findCompanyByExternalId(externalId: string): Promise<MfrCompany | null>;
|
|
createCompany(input: MfrCompany): Promise<MfrCompany>;
|
|
updateCompany(id: MfrId, input: Partial<MfrCompany>): Promise<MfrCompany>;
|
|
|
|
// Contacts
|
|
listContacts(query?: ODataQuery): Promise<MfrContact[]>;
|
|
getContact(id: MfrId): Promise<MfrContact>;
|
|
|
|
// Service objects
|
|
listServiceObjects(query?: ODataQuery): Promise<MfrServiceObject[]>;
|
|
getServiceObject(id: MfrId, expand?: string[]): Promise<MfrServiceObject>;
|
|
createServiceObject(input: MfrServiceObject): Promise<MfrServiceObject>;
|
|
|
|
// Appointments
|
|
listAppointments(query?: ODataQuery): Promise<MfrAppointment[]>;
|
|
getAppointment(id: MfrId): Promise<MfrAppointment>;
|
|
createAppointment(input: MfrAppointment): Promise<MfrAppointment>;
|
|
|
|
// Item types
|
|
listItemTypes(query?: ODataQuery): Promise<MfrItemType[]>;
|
|
getItemType(id: MfrId): Promise<MfrItemType>;
|
|
findItemTypeByExternalId(externalId: string): Promise<MfrItemType | null>;
|
|
createItemType(input: MfrItemType): Promise<MfrItemType>;
|
|
}
|
|
```
|
|
|
|
### 16.2 URL builders
|
|
|
|
```ts
|
|
function formatMfrId(id: MfrId): string {
|
|
const value = String(id).replace(/L$/, '');
|
|
if (!/^\d+$/.test(value)) throw new Error(`Invalid MFR numeric id: ${id}`);
|
|
return `${value}L`;
|
|
}
|
|
|
|
function entityUrl(entitySet: string, id: MfrId): string {
|
|
return `/odata/${entitySet}(${formatMfrId(id)})`;
|
|
}
|
|
```
|
|
|
|
### 16.3 OData query builder
|
|
|
|
```ts
|
|
export interface ODataQuery {
|
|
filter?: string;
|
|
select?: string[];
|
|
expand?: string[];
|
|
orderby?: string;
|
|
top?: number;
|
|
skip?: number;
|
|
count?: boolean;
|
|
}
|
|
|
|
function buildODataQuery(q: ODataQuery = {}): string {
|
|
const params = new URLSearchParams();
|
|
if (q.filter) params.set('$filter', q.filter);
|
|
if (q.select?.length) params.set('$select', q.select.join(','));
|
|
if (q.expand?.length) params.set('$expand', q.expand.join(','));
|
|
if (q.orderby) params.set('$orderby', q.orderby);
|
|
if (q.top != null) params.set('$top', String(q.top));
|
|
if (q.skip != null) params.set('$skip', String(q.skip));
|
|
if (q.count != null) params.set('$count', String(q.count));
|
|
const s = params.toString();
|
|
return s ? `?${s}` : '';
|
|
}
|
|
|
|
function odataStringLiteral(value: string): string {
|
|
return `'${value.replace(/'/g, "''")}'`;
|
|
}
|
|
```
|
|
|
|
### 16.4 HTTP implementation requirements
|
|
|
|
- Centralize HTTP transport.
|
|
- Add per-request timeout.
|
|
- Parse JSON only when `Content-Type` indicates JSON and body is non-empty.
|
|
- Include request ID/correlation ID in logs if server returns one.
|
|
- Throw typed errors with `status`, `method`, `url`, and redacted response body.
|
|
- Unit-test URL encoding and ID formatting.
|
|
|
|
## 17. OpenAPI starter definition
|
|
|
|
The following OpenAPI fragment is intentionally conservative. It models confirmed and strongly indicated routes but leaves unverified response schemas extensible.
|
|
|
|
```yaml
|
|
openapi: 3.1.0
|
|
info:
|
|
title: mfr Mobile Field Report Integration API
|
|
version: 0.1.0-draft
|
|
description: Draft integration contract. Verify against current mfr Postman collection and tenant metadata.
|
|
servers:
|
|
- url: https://portal.mobilefieldreport.com
|
|
security:
|
|
- basicAuth: []
|
|
components:
|
|
securitySchemes:
|
|
basicAuth:
|
|
type: http
|
|
scheme: basic
|
|
schemas:
|
|
MfrId:
|
|
type: string
|
|
pattern: '^\\d+$'
|
|
Location:
|
|
type: object
|
|
additionalProperties: true
|
|
properties:
|
|
AddressString: { type: string }
|
|
Postal: { type: string }
|
|
City: { type: string }
|
|
Country: { type: string }
|
|
Contact:
|
|
type: object
|
|
additionalProperties: true
|
|
properties:
|
|
Id: { $ref: '#/components/schemas/MfrId' }
|
|
FirstName: { type: string }
|
|
LastName: { type: string }
|
|
Telephone: { type: string }
|
|
MobilePhone: { type: string }
|
|
Email: { type: string, format: email }
|
|
Company:
|
|
type: object
|
|
additionalProperties: true
|
|
required: [Name]
|
|
properties:
|
|
Id: { $ref: '#/components/schemas/MfrId' }
|
|
Name: { type: string }
|
|
ExternalId: { type: string }
|
|
IsPhysicalPerson:
|
|
oneOf:
|
|
- type: boolean
|
|
- type: integer
|
|
enum: [0, 1]
|
|
Location: { $ref: '#/components/schemas/Location' }
|
|
MainContact: { $ref: '#/components/schemas/Contact' }
|
|
ServiceObject:
|
|
type: object
|
|
additionalProperties: true
|
|
required: [Name]
|
|
properties:
|
|
Id: { $ref: '#/components/schemas/MfrId' }
|
|
Name: { type: string }
|
|
ExternalId: { type: string }
|
|
CompanyId: { $ref: '#/components/schemas/MfrId' }
|
|
Location: { $ref: '#/components/schemas/Location' }
|
|
Contacts:
|
|
type: array
|
|
items: { $ref: '#/components/schemas/Contact' }
|
|
DeepCreateServiceRequestRequest:
|
|
type: object
|
|
additionalProperties: true
|
|
required: [Name]
|
|
properties:
|
|
Name: { type: string }
|
|
Description: { type: string }
|
|
State: { type: string }
|
|
Customer: { $ref: '#/components/schemas/Company' }
|
|
ServiceObjects:
|
|
type: array
|
|
items: { $ref: '#/components/schemas/ServiceObject' }
|
|
paths:
|
|
/mfr/ServiceRequest/Deep:
|
|
post:
|
|
operationId: createServiceRequestDeep
|
|
summary: Create a service request with nested customer and service objects
|
|
requestBody:
|
|
required: true
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/DeepCreateServiceRequestRequest'
|
|
responses:
|
|
'200':
|
|
description: Created or returned service request
|
|
content:
|
|
application/json:
|
|
schema:
|
|
type: object
|
|
additionalProperties: true
|
|
'201':
|
|
description: Created
|
|
/mfr/Document/UploadAndCreate:
|
|
post:
|
|
operationId: uploadAndCreateDocument
|
|
summary: Upload a document and create a document record
|
|
requestBody:
|
|
required: true
|
|
content:
|
|
multipart/form-data:
|
|
schema:
|
|
type: object
|
|
required: [file, options]
|
|
properties:
|
|
file:
|
|
type: string
|
|
format: binary
|
|
options:
|
|
type: string
|
|
description: JSON string with filename and association fields. Verify schema in Postman.
|
|
responses:
|
|
'200':
|
|
description: Uploaded document
|
|
content:
|
|
application/json:
|
|
schema:
|
|
type: object
|
|
additionalProperties: true
|
|
/odata/Companies:
|
|
get:
|
|
operationId: listCompanies
|
|
parameters:
|
|
- name: $filter
|
|
in: query
|
|
schema: { type: string }
|
|
- name: $top
|
|
in: query
|
|
schema: { type: integer, minimum: 1 }
|
|
- name: $skip
|
|
in: query
|
|
schema: { type: integer, minimum: 0 }
|
|
responses:
|
|
'200':
|
|
description: Company collection
|
|
post:
|
|
operationId: createCompany
|
|
requestBody:
|
|
required: true
|
|
content:
|
|
application/json:
|
|
schema: { $ref: '#/components/schemas/Company' }
|
|
responses:
|
|
'200': { description: Created company }
|
|
'201': { description: Created company }
|
|
/odata/Companies({id}L):
|
|
get:
|
|
operationId: getCompany
|
|
parameters:
|
|
- name: id
|
|
in: path
|
|
required: true
|
|
schema: { $ref: '#/components/schemas/MfrId' }
|
|
responses:
|
|
'200': { description: Company }
|
|
/odata/ServiceObjects:
|
|
get:
|
|
operationId: listServiceObjects
|
|
parameters:
|
|
- name: $filter
|
|
in: query
|
|
schema: { type: string }
|
|
- name: $expand
|
|
in: query
|
|
schema: { type: string }
|
|
responses:
|
|
'200': { description: Service object collection }
|
|
post:
|
|
operationId: createServiceObject
|
|
requestBody:
|
|
required: true
|
|
content:
|
|
application/json:
|
|
schema: { $ref: '#/components/schemas/ServiceObject' }
|
|
responses:
|
|
'200': { description: Created service object }
|
|
'201': { description: Created service object }
|
|
```
|
|
|
|
## 18. AI coding prompt to generate an SDK
|
|
|
|
Use this prompt with an AI coding tool after adding tenant-specific Postman details.
|
|
|
|
```text
|
|
Build a TypeScript SDK for the mfr Mobile Field Report API.
|
|
|
|
Requirements:
|
|
- Use Basic Auth over HTTPS.
|
|
- Make baseUrl configurable; default to https://portal.mobilefieldreport.com.
|
|
- Use /odata for entity CRUD and /mfr for special REST operations.
|
|
- Treat IDs as strings because mfr IDs can exceed JavaScript safe integer range.
|
|
- Append L to numeric OData path IDs, e.g. /odata/Companies(123L).
|
|
- Implement OData query builder supporting $filter, $select, $expand, $orderby, $top, $skip, $count.
|
|
- Implement createServiceRequestDeep using POST /mfr/ServiceRequest/Deep.
|
|
- Implement uploadDocument using multipart/form-data POST /mfr/Document/UploadAndCreate with fields file and options.
|
|
- Implement Companies, Contacts, ServiceObjects, Appointments, and ItemTypes list/get/create methods.
|
|
- Mark update/delete methods experimental unless verified in Postman.
|
|
- Include typed errors, retries for GET on 429/5xx, redacted logging, and tests for URL building.
|
|
- Do not assume exact response bodies; parse extensibly with additionalProperties.
|
|
```
|
|
|
|
## 19. Test plan
|
|
|
|
### 19.1 Unit tests
|
|
|
|
| Test | Expected result |
|
|
|---|---|
|
|
| `formatMfrId('123')` | `123L` |
|
|
| `formatMfrId('123L')` | `123L` |
|
|
| `formatMfrId('abc')` | throws validation error |
|
|
| OData string literal for `O'Brien` | `'O''Brien'` |
|
|
| Query builder with filter and expand | URL-encoded `$filter` and `$expand` parameters |
|
|
| Empty JSON response | returns `undefined` / void without JSON parse error |
|
|
| Error response with `Message` | typed error includes readable message |
|
|
| Upload document | multipart contains `file` and `options` fields |
|
|
|
|
### 19.2 Integration tests
|
|
|
|
Run these against a sandbox/test tenant:
|
|
|
|
1. Authenticate with invalid credentials and confirm 401 handling.
|
|
2. List companies with `$top=1`.
|
|
3. Create company with unique `ExternalId`.
|
|
4. Query same company by `ExternalId`.
|
|
5. Create service object linked to company.
|
|
6. Create service request with `/mfr/ServiceRequest/Deep` using a test template ID.
|
|
7. Upload a small text file to `/mfr/Document/UploadAndCreate`.
|
|
8. Attempt duplicate create and confirm idempotency behavior.
|
|
9. Test pagination with `$top` and `$skip`.
|
|
10. Verify datetime filters on appointments.
|
|
|
|
### 19.3 Production readiness checklist
|
|
|
|
- Current Postman collection exported and archived.
|
|
- Tenant-specific base URL confirmed.
|
|
- Authentication method confirmed.
|
|
- All required fields for create operations confirmed.
|
|
- Template IDs documented.
|
|
- Appointment type/user assignment semantics documented.
|
|
- Document upload `options` schema confirmed.
|
|
- Maximum upload size confirmed.
|
|
- Rate limit behavior confirmed.
|
|
- Update/delete support confirmed or intentionally disabled.
|
|
- Data protection and logging rules approved.
|
|
|
|
## 20. Known gaps to verify
|
|
|
|
The following must be verified before a production-grade implementation is declared complete:
|
|
|
|
1. Exact authentication method and whether API tokens are supported in addition to Basic Auth.
|
|
2. Current Postman collection endpoint list and examples.
|
|
3. Exact response bodies for create/update/delete and deep-create operations.
|
|
4. Whether OData returns plain arrays, `{ value: [...] }`, or another wrapper for all list endpoints.
|
|
5. Whether updates require `PUT`, `PATCH`, merge semantics, or ETags.
|
|
6. Whether delete operations are enabled for each entity.
|
|
7. Whether OData metadata (`$metadata`) is publicly available and complete.
|
|
8. Exact enum values for `State`, appointment `Type`, item `Type`, units, tags, and report definitions.
|
|
9. Exact document upload `options` schema and association model.
|
|
10. AMQP connection details and event schema.
|
|
11. UGL article import file format.
|
|
12. Navision/Dynamics kit installation and payload semantics.
|
|
|
|
## 21. Recommended next implementation sequence
|
|
|
|
1. Import the current Postman collection into the repository as documentation only.
|
|
2. Generate a minimal OpenAPI definition from verified endpoints.
|
|
3. Implement transport, authentication, error parser, and OData query builder.
|
|
4. Implement read-only list/get methods first.
|
|
5. Implement idempotent create flows using `ExternalId`.
|
|
6. Implement `/mfr/ServiceRequest/Deep` with tenant-provided template IDs.
|
|
7. Implement document upload after verifying `options` schema.
|
|
8. Add sandbox integration tests.
|
|
9. Add production-safe logging and credential handling.
|
|
10. Freeze a versioned SDK contract and publish.
|
|
|
|
## 22. cURL examples
|
|
|
|
### 22.1 List service objects with contacts
|
|
|
|
```bash
|
|
curl -u "$MFR_USERNAME:$MFR_PASSWORD" \
|
|
"https://portal.mobilefieldreport.com/odata/ServiceObjects?%24top=10&%24expand=Contacts"
|
|
```
|
|
|
|
### 22.2 Get a single service object
|
|
|
|
```bash
|
|
curl -u "$MFR_USERNAME:$MFR_PASSWORD" \
|
|
"https://portal.mobilefieldreport.com/odata/ServiceObjects(9875849220L)?%24expand=Contacts"
|
|
```
|
|
|
|
### 22.3 Create service request deeply
|
|
|
|
```bash
|
|
curl -u "$MFR_USERNAME:$MFR_PASSWORD" \
|
|
-H "Content-Type: application/json" \
|
|
-H "Accept: application/json" \
|
|
-X POST \
|
|
"https://portal.mobilefieldreport.com/mfr/ServiceRequest/Deep" \
|
|
--data @deep-service-request.json
|
|
```
|
|
|
|
### 22.4 Upload document
|
|
|
|
```bash
|
|
curl -u "$MFR_USERNAME:$MFR_PASSWORD" \
|
|
-X POST \
|
|
"https://portal.mobilefieldreport.com/mfr/Document/UploadAndCreate" \
|
|
-F "file=@report.pdf;type=application/pdf" \
|
|
-F 'options={"filename":"report.pdf"}'
|
|
```
|
|
|
|
## 23. Reference URLs
|
|
|
|
- Official mfr wiki: `https://faq.mobilefieldreport.com/de/wiki/beschreibung-schnittstelle`
|
|
- Public Postman documentation supplied: `https://documenter.getpostman.com/view/6932380/2sB3dWsn6U`
|
|
- OData overview: `https://www.odata.org/`
|
|
- OData query option documentation: Microsoft Learn OData query option documentation
|
|
- Public n8n community/API discussion for document upload
|
|
- Public mfr ecosystem connector references, used only as non-authoritative implementation evidence
|
|
|
|
## 24. Final note for AI coding tools
|
|
|
|
When generating code, distinguish between **transport mechanics** and **business certainty**. The transport mechanics (Basic Auth, OData URL building, JSON requests, multipart upload) can be implemented generically. Business-specific fields, enums, template IDs, and document association fields must remain configurable until confirmed against the current tenant and the live Postman collection.
|