Overview
ServiceMax (part of PTC) is field service management for equipment makers and operators. ServiceMax Core is a managed package (SVMXC__ objects) and Asset 360 (SVMXA360__) builds on Salesforce Field Service; both run inside your Salesforce org. This connector lets the broker open a ServiceMax work order when a machine raises an alarm, put run hours and other telemetry on the installed product, and show each technician’s open work orders, so service teams see what the machine sees.
The template has two connectors:
- ServiceMaxApi (
ServicemaxRestApi.lot), a client connector on your org’s My Domain with 26 events: 10 writes, 14 reads and 2 token events. It has noDELETEevents. Events starting withA360need Asset 360; the others need ServiceMax Core. - ServiceMaxNotificationReceiver (
cmms_servicemax_webhook.lot), an optional server connector on port9011that receives JSON callouts from a Flow or Apex in your org and publishes them on MQTT.
ServiceMax publishes no OpenAPI or Postman file of its own: its objects are ordinary Salesforce sObjects, read and written with the Salesforce REST API. The events were picked by hand from the PTC data dictionaries (Work Order, Installed Product, Technician/Equipment) and the Salesforce REST API Developer Guide.
Events
Replies arrive on the same topic with
/response appended. The SOQL behind the fixed query events:
-
ListOpenWorkOrdersByTechnician: -
ListOpenWorkOrdersByInstalledProduct: -
FindInstalledProductBySerial: -
ListTechnicians: -
A360ListOpenWorkOrdersByAsset:
Prerequisites
- A Salesforce org with ServiceMax Core installed (and Asset 360 for the
A360events). - An External Client App (or Connected App) with OAuth enabled. For server-to-server use enable the client credentials flow and set a Run As integration user that has the ServiceMax permission sets and access to the objects and fields you write.
- Your org’s My Domain URL, for example
https://acme.my.salesforce.com(sandboxhttps://acme--uat.sandbox.my.salesforce.com). ChangeBASE_ADDRESSto it.login.salesforce.comandtest.salesforce.comonly issue tokens; REST calls on them fail withURL_NOT_RESET. - Outbound HTTPS from the broker to that host.
- For notifications only: a public HTTPS URL that forwards to the broker’s port
9011.
Authentication
GetToken posts a form body to /services/oauth2/token on your My Domain. With the client credentials flow:
access_token and instance_url. Every other event sends Authorization: Bearer {value.json.access_token}, so each payload carries the token:
grant_type=urn:ietf:params:oauth:grant-type:jwt-bearer&assertion=<signed JWT>) and refresh tokens. Tokens expire with the org’s session timeout; on 401 INVALID_SESSION_ID call GetToken again. The example actions refresh it every hour.
Answers seen from the real Salesforce hosts without valid credentials: a fake client returns 400 {"error":"invalid_client_id"}, a fake JWT 400 invalid_grant, and any REST call with a fake token 401 [{"errorCode":"INVALID_SESSION_ID"}].
Payload conventions
access_token: the token fromGetToken.id: an 18-character Salesforce record Id (work ordera2x..., installed product, technician, Asset), or the query locator forQueryMore.query:fields=Id,Name,...for the get events, or""for all fields.q: a whole SOQL statement, URL-encoded (spaces as+).technician_id,installed_product_id,serial,asset_id: values the fixed queries put inside SOQL quotes.body: the JSON record, sent unchanged onPOSTandPATCHevents.
201 with {"id": "...", "success": true, "errors": []}; updates answer an empty 204. Errors are JSON arrays such as [{"errorCode": "REQUIRED_FIELD_MISSING", "message": "...", "fields": [...]}].
Create a work order from an alarm. SVMXC__Component__c is the installed product, SVMXC__Company__c the account:
SVMXC__Order_Status__c, SVMXC__Priority__c and SVMXC__Order_Type__c are picklists that each org configures, so read the allowed values with DescribeWorkOrder first:
Coreflux_Alarm_Id__c) to the work order and use UpsertWorkOrderByExternalId, or check ListOpenWorkOrdersByInstalledProduct first.
Telemetry
ServiceMax Core has no standard telemetry field onSVMXC__Installed_Product__c. Add the custom fields you need (for example Run_Hours__c and Last_Telemetry_At__c) and write them with UpdateInstalledProduct, or for many machines at once with UpdateRecordsBatch (up to 200 records per call). Map a machine’s serial number to its record with FindInstalledProductBySerial.
A360SaveTechnicalAttributes creates missing attributes, records the time of each reading and finds the Asset by Id, name or an identifier field (200 entries per call):
Notification receiver
Deploycmms_servicemax_webhook.lot only if you want the org to push changes to the broker. It opens HTTP port 9011.
Salesforce has no built-in JSON webhook. Build a record-triggered Flow with an HTTP Callout action (or an Apex callout) on
SVMXC__Service_Order__c or SVMXC__Installed_Product__c, point a Named Credential at the public URL of the matching path, and send the fields you need as JSON.
Connector
Example actions
These actions refresh the token every hour, open a work order when a compressor alarm is raised, write run hours to the installed product, and poll one technician’s open work orders.Notes
- The connector pins API
v65.0. Change the version in the endpoints to move up;ListApiVersionsshows what your org serves. - “Open” in the fixed queries means
SVMXC__Closed_On__cis empty. If your org cancels work orders without a close date, useQuerywith your own status filter. - Every call counts against the org’s rolling 24-hour API allocation (
GetLimitsshowsDailyApiRequests). Over it, Salesforce answers403 REQUEST_LIMIT_EXCEEDED. Poll every few minutes, not per message. - Queries return up to 2000 rows per page; when
doneis false, pass the end ofnextRecordsUrltoQueryMore. - Writes fire the org’s ServiceMax triggers, flows, SLA clocks and dispatch rules.
PublishPlatformEventneeds a platform event you define (for exampleCoreflux_Telemetry__e) that Flows or Apex triggers subscribe to.
Not covered
Deletes, work detail lines and parts, ServiceMax SFM and dispatch services, Outbound Messages, Pub/Sub API and streaming subscriptions, Bulk API, files, and Asset 360 maintenance plans, warranties and service contracts. All are reachable through the Salesforce APIs and can be added to a fork.Status
Technical preview, blocked. The template passes 363 of 363 smoke checks: static checks of both connectors against the PTC data dictionaries and Salesforce REST docs, every event against a local mock of the Salesforce REST API, negative controls, and calls to real Salesforce hosts without valid credentials (the token endpoint rejects fake clients, and every packaged call with a fake token stops at401 INVALID_SESSION_ID). Salesforce checks the token before it connectors a request, so those calls cannot prove that the ServiceMax objects exist in an org. No call has run yet against a real ServiceMax org.
To finish testing, use a sandbox with ServiceMax Core and an External Client App with the client credentials flow: set SALESFORCE_INSTANCE_URL, SALESFORCE_CLIENT_ID, SALESFORCE_CLIENT_SECRET and SERVICEMAX_TEST_INSTALLED_PRODUCT (and optionally SERVICEMAX_TEST_IP_FIELD), then run SERVICEMAX_ALLOW_WRITE=1 python3 smoke_servicemax.py. It reads limits, both describes and the technicians, creates a work order called “Coreflux smoke test (safe to delete)”, reads it back, lists open work orders on the installed product and writes one installed product field. Then deploy the connector on a broker and do an MQTT round trip.

