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>
40 KiB
title, subtitle, author, date
| title | subtitle | author | date |
|---|---|---|---|
| mfr Mobile Field Report - Interface Description for AI Coding Tools | REST/OData integration contract derived from the public mfr wiki, public Postman documentation context, and public implementation evidence | Prepared for AI-assisted development | 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
- Official mfr wiki page:
https://faq.mobilefieldreport.com/de/wiki/beschreibung-schnittstelle - 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:
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:
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
Authorizationheader. - Support tenant-specific credentials.
- Fail fast if credentials are missing.
Example environment variables:
MFR_BASE_URL="https://portal.mobilefieldreport.com"
MFR_USERNAME="service-account@example.com"
MFR_PASSWORD="***"
5.2 Default headers
For JSON operations:
Accept: application/json
Content-Type: application/json
Authorization: Basic <base64(username:password)>
For file upload:
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:
/odata/ServiceObjects(9875849220L)
/odata/Companies(1234567890L)
Implementation guidance:
- Treat entity IDs as 64-bit numeric identifiers.
- Use
stringorbigintin JavaScript/TypeScript to avoid precision loss. - Public examples append an
Lsuffix 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:
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
/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:
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/EntitySetto create.GET /odata/EntitySet(idL)to read.PUT /odata/EntitySet(idL)orPATCH /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:
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
export interface MfrLocation {
Id?: MfrId;
AddressString?: string;
Postal?: string;
City?: string;
Country?: string; // e.g. DE
Latitude?: number;
Longitude?: number;
}
7.4 Contact model
export interface MfrContact {
Id?: MfrId;
FirstName?: string;
LastName?: string;
Telephone?: string;
MobilePhone?: string;
Email?: string;
CompanyId?: MfrId;
}
7.5 Service object model
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
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
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
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
POST /mfr/ServiceRequest/Deep
Host: portal.mobilefieldreport.com
Content-Type: application/json
Accept: application/json
Full observed URL:
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
{
"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:
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
Customeror a knownCustomerIdif 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
POST /mfr/Document/UploadAndCreate
Host: portal.mobilefieldreport.com
Content-Type: multipart/form-data
Accept: application/json
Full observed URL:
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
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:
export interface UploadDocumentOptions {
filename?: string;
serviceRequestId?: MfrId;
jobId?: MfrId;
serviceObjectId?: MfrId;
entityId?: MfrId;
entityType?: string;
description?: string;
[key: string]: unknown;
}
Then expose:
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.
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:
{
"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.
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:
{
"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.
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:
{
"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.
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.
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:
{
"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.
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:
{
"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:
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:
{
"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:
- Build nested customer object.
- Build at least one service object with location and contact.
- Add
CreateFromServiceRequestTemplateIdif a template should be applied. - Set
Stateto an accepted state such asReadyForScheduling. POST /mfr/ServiceRequest/Deep.- Store returned mfr IDs and external IDs in the source system.
11.2 Synchronize companies from ERP
- For each ERP customer, query by
ExternalId:GET /odata/Companies?$filter=ExternalId eq 'ERP-123'. - If found, update the existing company if update is supported.
- If not found, create via
POST /odata/Companies. - Store the returned mfr
Idas a cross-reference. - Use rate limiting and retry on transient 5xx/429 errors.
11.3 Create service object under an existing customer
- Resolve customer/company by external ID.
POST /odata/ServiceObjectswithCompanyIdandLocation.- Optionally attach contacts through nested data or OData link operations if supported.
- Query with
$expand=Contactsto verify relationships.
11.4 Create appointment for an existing job
- Resolve service request/job ID.
- Resolve technician/user if required by tenant.
- Resolve appointment type if tenant requires enumerated types.
POST /odata/Appointmentswith start/end time and links.- Read back the appointment and verify assigned technician/status.
11.5 Upload document for a job
- Read file from source system.
- Build multipart form with
fileandoptions. - Include
filenameand association fields required by tenant/Postman collection. POST /mfr/Document/UploadAndCreate.- Store returned document ID.
- 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:
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
ExternalIdto 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:
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:
const startUtc = zonedTimeToUtc('2026-06-10 08:00', 'Europe/Berlin').toISOString();
Payload example:
{
"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
$expandand$selectto 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
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
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
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-Typeindicates 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.
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.
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:
- Authenticate with invalid credentials and confirm 401 handling.
- List companies with
$top=1. - Create company with unique
ExternalId. - Query same company by
ExternalId. - Create service object linked to company.
- Create service request with
/mfr/ServiceRequest/Deepusing a test template ID. - Upload a small text file to
/mfr/Document/UploadAndCreate. - Attempt duplicate create and confirm idempotency behavior.
- Test pagination with
$topand$skip. - 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
optionsschema 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:
- Exact authentication method and whether API tokens are supported in addition to Basic Auth.
- Current Postman collection endpoint list and examples.
- Exact response bodies for create/update/delete and deep-create operations.
- Whether OData returns plain arrays,
{ value: [...] }, or another wrapper for all list endpoints. - Whether updates require
PUT,PATCH, merge semantics, or ETags. - Whether delete operations are enabled for each entity.
- Whether OData metadata (
$metadata) is publicly available and complete. - Exact enum values for
State, appointmentType, itemType, units, tags, and report definitions. - Exact document upload
optionsschema and association model. - AMQP connection details and event schema.
- UGL article import file format.
- Navision/Dynamics kit installation and payload semantics.
21. Recommended next implementation sequence
- Import the current Postman collection into the repository as documentation only.
- Generate a minimal OpenAPI definition from verified endpoints.
- Implement transport, authentication, error parser, and OData query builder.
- Implement read-only list/get methods first.
- Implement idempotent create flows using
ExternalId. - Implement
/mfr/ServiceRequest/Deepwith tenant-provided template IDs. - Implement document upload after verifying
optionsschema. - Add sandbox integration tests.
- Add production-safe logging and credential handling.
- Freeze a versioned SDK contract and publish.
22. cURL examples
22.1 List service objects with contacts
curl -u "$MFR_USERNAME:$MFR_PASSWORD" \
"https://portal.mobilefieldreport.com/odata/ServiceObjects?%24top=10&%24expand=Contacts"
22.2 Get a single service object
curl -u "$MFR_USERNAME:$MFR_PASSWORD" \
"https://portal.mobilefieldreport.com/odata/ServiceObjects(9875849220L)?%24expand=Contacts"
22.3 Create service request deeply
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
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.