Hi ServiceM8 team — hoping someone from your end can pick this up. There are two existing community threads reporting exactly this issue, both with zero replies from ServiceM8:
- Custom field deletion (Joe Beaver, Feb 2018) — 8+ years old, never answered.
- Custom fields numeric conversion / delete failure (Anthony Archer, Aug 2023) — 2.5+ years old, never answered.
I’m hitting the same wall in 2026 while building a public OAuth 2.0 add-on. List and Create work. Delete is fully broken — the API rejects every request shape with errorCode: 100, "Invalid UUID", even though the UUID is the one the API itself just returned from GET.
Reproduction
A single OAuth 2.0 access token used for all requests below. Scope: manage_jobs manage_customers.
1. Create a Job custom field
curl -X POST "https://api.servicem8.com/custom_fields_1.0" \
-H "Authorization: Bearer <ACCESS_TOKEN>" \
-H "Content-Type: application/json" \
-d '{"object_name":"Job","field_name":"test","display_name":"TEST","field_type":"Text","max_length":50}'
Response: {"errorCode":0,"message":"OK","UUID":"5671be47-9ebb-49d3-bb9b-242077a141fb"} — works.
2. List custom fields
curl "https://api.servicem8.com/custom_fields_1.0" \
-H "Authorization: Bearer <ACCESS_TOKEN>" \
-H "Accept: application/json"
Response (HTTP 200):
[{
"uuid": "5671be47-9ebb-49d3-bb9b-242077a141fb",
"object": "job",
"display_name": "TEST",
"field_name": "test",
"field_type": "Text",
"is_unique": 0,
"sort_priority": 0,
"max_length": 50
}]
So the API confirms the field exists with that exact UUID.
3. Try to delete that exact UUID — every shape fails
I tried every reasonable interpretation of the docs (https://developer.servicem8.com/reference/delete_custom_field) and standard ServiceM8 REST conventions:
| # | Request shape | Result |
|---|---|---|
| 1 | DELETE /custom_fields_1.0 body UUID=<uuid> (form-urlencoded — exactly what your docs / API Explorer show) |
400 {"errorCode":100,"message":"Invalid UUID"} |
| 2 | Same as 1, but uuid=<uuid> (lowercase key) |
400 Invalid UUID |
| 3 | DELETE /custom_fields_1.0 JSON body {"UUID":"<uuid>"} |
400 Invalid UUID |
| 4 | DELETE /custom_fields_1.0?UUID=<uuid> (query string) |
400 Invalid UUID |
| 5 | DELETE /custom_fields_1.0/<uuid> (path style, like /api_1.0/job/<uuid>.json) |
400 Invalid UUID |
| 6 | POST /custom_fields_1.0 with header X-HTTP-Method-Override: DELETE |
400 {"errorCode":110,"message":"expected valid JSON in request body"} (proves POST is reachable on the same path) |
| 7 | DELETE with --data-urlencode "UUID=<uuid>" (matches your API Explorer’s curl exactly) |
400 Invalid UUID |
The exact request from your own API Explorer page also fails:
curl --request DELETE \
--url https://api.servicem8.com/custom_fields_1.0 \
--header 'Authorization: Bearer <ACCESS_TOKEN>' \
--header 'accept: application/json' \
--header 'content-type: application/x-www-form-urlencoded' \
--data UUID=5671be47-9ebb-49d3-bb9b-242077a141fb
→ HTTP 400 {"errorCode":100,"message":"Invalid UUID"}.
Response headers indicate the 400 is generated by ServiceM8 (not CloudFront / not a CORS preflight artefact):
HTTP/1.1 400 Bad Request
Content-Type: application/json
Server: nginx
X-Cache: Error from cloudfront
What I’d like to know
- Is
DELETE /custom_fields_1.0actually wired up for OAuth 2.0 public applications? Given the 2018 and 2023 reports above, it looks like this endpoint has been broken for at least 8 years. - If it does work, what’s the correct request shape? None of the seven variants above match. The published docs say body-style with
UUIDkey, which is what I send and what fails. - Is there a hidden scope or capability flag (something analogous to
manage_custom_fields) that public apps need to include in their OAuth scope to be allowed to delete? Your published scope table doesn’t list a custom-fields scope; we’re usingmanage_jobs manage_customersbecause the fields are on Job/Company. - If there’s no fix coming, please update the Custom Fields API docs to clearly state that DELETE is not supported via OAuth 2.0, so developers stop spending hours debugging a working request against a non-functional endpoint.
Documentation, scope tables, OpenAPI spec, and the in-product API Explorer all imply this should work. It doesn’t, and it hasn’t for a very long time. Could we please get a real answer this time?
Thanks.