Skip to content

Enterprise Metadata — Pitfalls ​

detail=true and detail=false return different shapes ​

Symptom. Code that parses detail=true responses crashes when called with detail=false (or vice versa) — missing keys, wrong types.

Cause. GET /custom-fields and GET /custom-actions return materially different shapes per detail mode. Summary mode returns a handful of keys; detailed mode adds descriptions, field schemas, and (for fields) options[]/tags[]/date formats.

Both endpoints also return a bare array, not an object wrapping a customFields or customActions key.

Fix. Pick one mode for your client and stick with it. If you don't need the extra metadata, default to detail=false for smaller payloads. If you read both modes, branch on the presence of the description key on each item — custom fields carry no id in either mode.

Sending API names with wrong casing ​

Symptom. GET /custom-fields/{apiName} returns 404 FIELD_NOT_FOUND for a field that exists in the dashboard.

Cause. API names are case-sensitive. Contact_Source, contact_source, and CONTACT_SOURCE are three different lookups.

Fix. Get the canonical casing from GET /custom-fields, or copy the value from Settings → Lead Fields → click the field → Properties → API name in the dashboard.

Treating archived statuses and lost reasons as valid values ​

Symptom. Lead create or update with status: "Old Status" succeeds but the field is missing on the resulting lead.

Cause. Archived statuses and lost reasons are returned by GET /lead-stage-pipeline so historical leads remain readable, but they cannot be used as values on new or updated leads. Both lists mix active and archived entries, distinguished only by the archived flag on each one.

Worse, an archived status doesn't fail loudly on update — the lead's status is silently reverted to its previous value.

Fix. Filter on archived: false when building the set of valid values to expose to your users.

Looking up a custom action with the wrong code format ​

Symptom. GET /custom-actions/{code} returns 404 even though the action exists.

Cause. The path parameter takes the full code (ACTION_1001), but the Sync action payload uses the bare numeric ("1001"). It's easy to mix them up.

Fix. When calling the lookup endpoint, use the full ACTION_<numeric> form. When sending an action via create, strip the prefix.

diff
# Lookup endpoint
- /custom-actions/1001
+ /custom-actions/ACTION_1001

# Action create payload
- { "type": "ACTION_1001" }
+ { "type": "1001" }

Caching metadata forever ​

Symptom. A custom field renamed in the dashboard breaks payloads, or a newly-added option silently mismatches.

Cause. Custom fields, custom actions, and the lead stage pipeline are mutable in the dashboard. Stale caches drift from reality.

Fix. Refresh metadata at integration startup and on a slow cadence (e.g. every 15 minutes). The endpoints are cheap and idempotent — there's no rate concern about polling them periodically.

Confusing leadIdentifierFieldId with the API name ​

Symptom. Lead payloads use leadIdentifierFieldId (e.g. phone) as the key — works for default workspaces but fails for workspaces using a custom identifier with a different ID and API name.

Cause. leadIdentifierFieldId from GET /metadata is a field ID, not a guaranteed API name. For default phone identifiers the two happen to match, but for custom identifiers they can differ.

Joining the two yourself isn't possible either — GET /custom-fields doesn't return field IDs in either detail mode, so there is nothing to match leadIdentifierFieldId against.

Fix. Ignore leadIdentifierFieldId and read the flag the fields endpoint already provides: the entry with isLeadid: true is the identifier, and its apiName is the payload key. The Examples page shows the full pattern.