Capture the actual error
Save HTTP status, response body, correlation ID, endpoint, object type, and a redacted payload. The useful field is often inside `message`, `errors`, or a property-specific context. A request ID without the body is not enough to identify a bad option or missing field.
Example shape:
Do not log access tokens, refresh tokens, full personal data, or an unrestricted production payload while diagnosing.
{
"status": "error",
"message": "Invalid input",
"correlationId": "REDACTED-ID",
"category": "VALIDATION_ERROR",
"errors": [{"field": "dealstage", "message": "..."}]
}
Use internal names, not labels
The UI label “Lead Source” may map to an internal name such as `hs_lead_status` or a custom name chosen when the property was created. API bodies use the internal name. Labels can be translated, renamed, or duplicated; internal names are the stable contract for requests.
Fetch the object schema for the exact portal and environment. Compare each outgoing key against the response. Remove stale keys from templates. If a property was deleted and recreated, its internal name and allowed values may differ even when the label looks identical.
Deal-specific checks
Deals often fail because a pipeline and stage are not valid together. A stage value from one pipeline may be rejected in another. Use the internal IDs returned by the current portal configuration. Verify amount is represented in the expected numeric form and dates are not formatted as human text.
Some portals or business processes require additional properties. A create call that worked in a sandbox may fail in production because the pipeline, stage, currency, custom property, or required rule differs. Compare schemas and configuration between portals rather than copying a successful payload blindly.
Diagnostic deal body:
Use the exact values returned by the target portal; the example is only a shape.
{
"properties": {
"dealname": "Website renewal",
"pipeline": "default",
"dealstage": "qualifiedtobuy",
"amount": "2500"
}
}
Separate associations from object creation
When debugging, create the contact or deal without associations. If the object succeeds, add the association in a second request and inspect the association type ID. This isolates an invalid object property from an invalid relationship definition. Once the two calls are known to work, combine them only if the endpoint and payload officially support that shape.
Common payload mistakes
The API expects properties under a `properties` object. Sending them at the top level, using `propertyValues`, nesting a JSON string, or passing an array where a scalar is expected can cause validation failures. Empty strings may not be equivalent to null. Omit a property when it is unknown instead of sending a guessed value.
Enum fields require an allowed internal option value, not the visible label. Multi-checkbox values have their own serialization rules. Numeric fields reject currency symbols and localized separators. Date fields reject ambiguous formats. URL fields may reject a value that is not a valid URL.
Warning: never “solve” a validation error by disabling validation or writing the UI label into the body. That creates brittle data and can turn a mapping error into corrupted CRM records.
A deterministic debugging procedure
First replay the failing request against a non-production test record with the same endpoint and token type. Second, fetch the property schema. Third, reduce the body to the smallest valid payload. Fourth, add keys in a fixed order and stop at the first failure. Fifth, record the accepted internal value and update the source mapping.
Keep a contract test for every object type. It should assert that required property names exist, enum values are current, and a dry-run fixture passes local validation. Refresh schema metadata on a schedule but cache it briefly so a temporary metadata failure does not block every write.
Manual QA checklist
Test minimal contact, full contact, custom text, invalid enum, invalid date, blank value, duplicate email, minimal deal, wrong pipeline/stage combination, invalid amount, missing required field, and association added separately. Test both private-app and OAuth credentials if both are supported.
Verify that 400 responses enter a review queue rather than an infinite retry loop. Confirm logs show field names but redact personal values where possible. Check that a failed create does not cause a second create on the next run. Re-run a repaired payload and then fetch the record to verify the persisted values.
Production guardrails
Keep a versioned mapping for every portal or business unit that has different pipelines and custom properties. Refresh the mapping when an administrator changes an option, but do not let an unreviewed schema change automatically broaden a write payload. A schema cache miss should pause the affected job and create an actionable alert.
Before enabling a new field, send a canary request with a known test record. Compare the response with a subsequent GET and verify that the value was stored in the intended property. If the API accepts a value but a workflow later transforms it, test the final CRM behavior too. This catches valid-but-wrong mappings that a status-code check misses.
Maintain a dead-letter record containing the object type, source record key, failing field, sanitized message, attempt count, and schema version. An operator should be able to repair the mapping and replay one item without restarting the whole import.
Separate validation from duplicate handling
A contact create that fails because an email already identifies an existing record is a different workflow from a payload with an invalid property. Search by the agreed unique key, decide whether to update or stop, and do not let a broad `VALIDATION_ERROR` catch block create a second contact. For deals, keep pipeline and stage validation together so a stage from another pipeline is never silently remapped.
Where these facts come from