Issue with creating property owner in site contacts

We integrate with ServiceM8 via OAuth (manage_customer_contacts scope) to auto-create site companies (Use Sites / parent_company_uuid) and add a Property Owner contact from work-order data when a new site is created.

This has recently changed

Expected behaviour

When we POST to companycontact.json with:

{

"company_uuid": "<new-site-uuid>",

"first": "Owner Test",

"type": "Property Owner",

"is_primary_contact": "0"

}

…we expect the stored contact to have type: "Property Owner" and is_primary_contact: "0".

Actual behaviour

On a newly created site with no existing company contacts, ServiceM8 stores the contact as:

{

"type": "BILLING",

"is_primary_contact": "1",

"first": "Owner Test",

"last": ""

}

This happens even when we explicitly send type: "Property Owner" and is_primary_contact: "0". The API returns 200 / { "errorCode": 0, "message": "OK" }, but the created record is coerced to primary BILLING.

Reproduction (confirmed 18 Jun 2026)

  1. Create a new site company:

POST /api_1.0/company.json

{

  "active": 1,

  "name": "Test Site Address",

  "address": "123 Test St",

  "parent_company_uuid": "<parent-company-uuid>"

}

POST Property Owner as the first contact on that site:

POST /api_1.0/companycontact.json

{

  "company_uuid": "<company-uuid>",

  "first": "Owner Test 2",

  "type": "Property Owner",

  "is_primary_contact": "0"

}
  1. GET contacts:

GET /api_1.0/companycontact.json?$filter=company_uuid eq ‘<company-uuid’ and active eq 1

Result: type: "BILLING", is_primary_contact: "1" — not Property Owner.

Earlier test (without is_primary_contact)

Same outcome on another fresh site (<company-uuid>): sent type: "Property Owner" only → stored as BILLING + is_primary_contact: "1".

When it does work

If the site already has other contacts, a Property Owner POST with is_primary_contact: "0" is stored correctly as type: "Property Owner".

What we ruled out on our side

  • We are not sending type: "BILLING" for the owner — only "Property Owner".
  • Our template does not map owner name into job billing contact fields (billingContactFirstName is empty).
  • Job contacts (jobcontact.json) are separate; on our test job only JOB + Property Manager were created — the owner does not appear on jobcontact.json.
  • The incorrect BILLING row appears on companycontact.json for the site company_uuid, timestamped at site-creation time (before job contacts).

Impact

Client look for Property Owner on site contacts in the ServiceM8 UI. The owner name appears under BILLING instead, so it looks like the Property Owner was never created. c the When the Client billing attention is set to “Property Owner, C/- Company Name” it causes it t So not have the company name

Questions for ServiceM8

  1. Is it by design that the first companycontact on a new site is always forced to type: "BILLING" and is_primary_contact: "1", regardless of the type and is_primary_contact values in the POST body?
    And did logic change recently
  2. What is the supported API pattern to create a Property Owner contact as the first (or only) contact on a newly created site company?
  3. Should we use a different type value, a two-step create-then-update (PUT), or always seed another contact first?
  4. Is this behaviour documented anywhere in the API reference for companycontact.json?

Environment

  • API: https://api.servicem8.com/api_1.0/
  • Auth: OAuth 2.0 (integration app) with customer contact permissions
  • Use case: Sites add-on enabled, site companies under a parent agency/head-office company

Happy to provide additional request/response logs (with credentials redacted) if helpful.

Thanks.

Hi Michael,

From the documented Client Sites behaviour, a site shouldn’t have its own billing contact because billing contacts come from the head office, and adding a new site only captures site contact information because billing is handled through the head office. A first contact on a site being saved as BILLING doesn’t line up with that documented model, and we’re treating this pattern as something Support and engineering need to review rather than expected API behaviour.

The POST /companycontact.json endpoint requires the manage_customer_contacts OAuth scope, returns the created contact UUID in the x-record-uuid response header, and documents type as a string with common values including BILLING and JOB. The same API reference documents is_primary_contact as 1 for primary and 0 for non-primary, with a company intended to have only one active primary contact.

As an interim API pattern, create the contact, use the returned contact UUID, then immediately re-read the record and update it via POST /companycontact/{uuid}.json if the stored values were coerced. We can’t point to a documented API pattern that requires seeding another contact first, and the current companycontact reference doesn’t document a rule that the first site contact must be forced to BILLING or primary.

Please email the redacted request/response payloads, timestamps, affected company/contact UUIDs, and app ID to support@servicem8.com so we can investigate this against the API logs.

Thanks,
Cody