{"templateId":"../@theme/Templates/BlogWithAuthors","sharedDataIds":{"sidebar":"sidebar-blog/sidebars.yaml"},"props":{"metadata":{"markdoc":{"tagList":[]},"type":"markdown"},"seo":{"title":"Why morphing exists","description":"Developer documentation for the Caraer platform. REST API reference, Bearer authentication, tutorials, and guides for records, forms, webpages, webhooks, and automations.","llmstxt":{"hide":false,"sections":[{"title":"Table of contents","includeFiles":["**/*"],"excludeFiles":[]}],"excludeFiles":[]}},"dynamicMarkdocComponents":[],"compilationErrors":[],"ast":{"$$mdtype":"Tag","name":"article","attributes":{},"children":[{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Morphing lets one record exist in multiple object contexts without duplicating"," ","data. This post explains how ",{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["primary object"]}," and ",{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["morph objects"]}," work,"," ","how object-level ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["extendsTo"]}," configuration drives automatic morphing, and how"," ","to model a shared ",{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["person"]}," record that is visible as both ",{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["contact"]}," and"," ",{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["candidate"]},"."]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"why-morphing-exists","__idx":0},"children":["Why morphing exists"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["In many Caraer setups, the same real-world entity appears in more than one"," ","object:"]},{"$$mdtype":"Tag","name":"ul","attributes":{},"children":[{"$$mdtype":"Tag","name":"li","attributes":{},"children":["A person can be a ",{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["candidate"]}," and later also a ",{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["contact"]}]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["A lead can become a ",{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["candidate"]}," without copying ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["firstname"]},", ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["email"]},", and"," ","other shared fields"]}]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Without morphing, you would duplicate records or maintain fragile cross-links."," ","Morphing solves this by keeping ",{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["one record"]}," (one UUID, one set of property"," ","values) while making it visible in multiple object contexts."]},{"$$mdtype":"Tag","name":"hr","attributes":{},"children":[]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"the-mental-model","__idx":1},"children":["The mental model"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Every record has:"]},{"$$mdtype":"Tag","name":"div","attributes":{"className":"md-table-wrapper"},"children":[{"$$mdtype":"Tag","name":"table","attributes":{"className":"md"},"children":[{"$$mdtype":"Tag","name":"thead","attributes":{},"children":[{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"th","attributes":{"data-label":"Concept"},"children":["Concept"]},{"$$mdtype":"Tag","name":"th","attributes":{"data-label":"Property"},"children":["Property"]},{"$$mdtype":"Tag","name":"th","attributes":{"data-label":"Meaning"},"children":["Meaning"]}]}]},{"$$mdtype":"Tag","name":"tbody","attributes":{},"children":[{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Primary object"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["primary_object"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["The object the record was created as. This is the record's home type."]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Morph objects"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["morph_objects"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Additional object contexts where the record should also appear. Semicolon-separated object names, e.g. ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["contact;candidate"]},"."]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["All objects"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Derived"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Primary object + morph objects. Used for validation and unique checks."]}]}]}]}]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Important rules:"]},{"$$mdtype":"Tag","name":"ul","attributes":{},"children":[{"$$mdtype":"Tag","name":"li","attributes":{},"children":["The ",{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["primary object is never a morph object"]},"."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Morph objects are ",{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["additional visibility contexts"]},", not separate records."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["A morphed record still has ",{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["one UUID"]}," and ",{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["one value set"]},"."]}]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["When a record is morphed, Caraer makes it available in every object listed in"," ",{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["all objects"]},". That is why the same record appears when you list contacts,"," ","candidates, or persons — you are querying different object views of the same"," ","underlying data."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Example record after morphing:"]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"json","header":{"controls":{"copy":{}}},"source":"{\n  \"uuid\": \"RECORD_UUID\",\n  \"primary_object\": \"person\",\n  \"morph_objects\": \"contact;candidate\",\n  \"properties\": {\n    \"firstname\": \"Alex\",\n    \"email\": \"alex@example.com\"\n  }\n}\n","lang":"json"},"children":[]},{"$$mdtype":"Tag","name":"hr","attributes":{},"children":[]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"how-morphing-works","__idx":2},"children":["How morphing works"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["When a record is saved or morphed, Caraer:"]},{"$$mdtype":"Tag","name":"ol","attributes":{},"children":[{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Resolves the record's primary object and morph objects."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Applies default morph objects from the primary object's ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["extendsTo"]}," ","configuration (see below)."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Validates morph rules (no duplicate primary, trait limits)."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Persists ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["primary_object"]}," and ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["morph_objects"]}," on the record."]}]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["On create and update, morphing runs automatically as part of the save flow."," ","You can also morph an existing record explicitly through the API."]},{"$$mdtype":"Tag","name":"hr","attributes":{},"children":[]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"object-level-configuration-extendsto","__idx":3},"children":["Object-level configuration: ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["extendsTo"]}]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Objects can define default morph targets through ",{"$$mdtype":"Tag","name":"strong","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["extendsTo"]}]},". When"," ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["person"]}," has ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["extendsTo: [contact, candidate]"]},":"]},{"$$mdtype":"Tag","name":"ul","attributes":{},"children":[{"$$mdtype":"Tag","name":"li","attributes":{},"children":["A record created with primary object ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["person"]}," automatically gets"," ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["contact"]}," and ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["candidate"]}," as morph objects."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["You do not need to call the morph endpoint for every new person record."]}]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["extendsTo"]}," is schema configuration on the ",{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["object"]},", not per-record data."," ","Use it when a primary type should always be visible in other object contexts."]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"syncing-existing-records-after-schema-changes","__idx":4},"children":["Syncing existing records after schema changes"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["If you add or change ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["extendsTo"]}," on an object, existing records are not"," ","updated automatically. Call:"]},{"$$mdtype":"Tag","name":"ul","attributes":{},"children":[{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["POST /api/v2/objects/{uuid}/syncMorphObjects"]}]}]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["This re-applies morph configuration for existing records that belong to that"," ","object."]},{"$$mdtype":"Tag","name":"hr","attributes":{},"children":[]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"example-person--contact-and-candidate","__idx":5},"children":["Example: person → contact and candidate"]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"scenario","__idx":6},"children":["Scenario"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["You want:"]},{"$$mdtype":"Tag","name":"ul","attributes":{},"children":[{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["person"]}," to hold shared identity fields (",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["firstname"]},", ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["lastname"]},", ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["email"]},")"]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["candidate"]}," to expose recruitment-specific fields and views"]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["contact"]}," to expose CRM-specific fields and views"]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["One underlying record to appear in all relevant lists"]}]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"recommended-setup","__idx":7},"children":["Recommended setup"]},{"$$mdtype":"Tag","name":"ol","attributes":{},"children":[{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Create three objects: ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["person"]},", ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["candidate"]},", ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["contact"]},"."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Attach shared properties (",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["firstname"]},", ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["lastname"]},", ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["email"]},") to all three"," ","objects, or at least to ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["person"]}," and the specialized types."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Configure ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["person.extendsTo = [contact, candidate]"]},"."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Create records with primary object ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["person"]},"."]}]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Result for a new person record:"]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"json","header":{"controls":{"copy":{}}},"source":"{\n  \"primary_object\": \"person\",\n  \"morph_objects\": \"contact;candidate\"\n}\n","lang":"json"},"children":[]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["The record is visible in person, contact, and candidate list views."]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"querying-the-same-record-in-different-contexts","__idx":8},"children":["Querying the same record in different contexts"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Use ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["mainObject"]}," to choose which object view you are listing:"]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"bash","header":{"controls":{"copy":{}}},"source":"# Same record appears here because it is morphed into candidate\ncurl -X POST \"https://your-company.caraer.com/api/v2/records/index\" \\\n  -H \"Authorization: Bearer YOUR_ACCESS_TOKEN\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"page\": 1,\n    \"limit\": 25,\n    \"mainObject\": \"candidate\"\n  }'\n","lang":"bash"},"children":[]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"bash","header":{"controls":{"copy":{}}},"source":"# Same UUID, different object context\ncurl -X POST \"https://your-company.caraer.com/api/v2/records/index\" \\\n  -H \"Authorization: Bearer YOUR_ACCESS_TOKEN\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"page\": 1,\n    \"limit\": 25,\n    \"mainObject\": \"contact\"\n  }'\n","lang":"bash"},"children":[]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"bash","header":{"controls":{"copy":{}}},"source":"# List all persons, including those morphed into other types\ncurl -X POST \"https://your-company.caraer.com/api/v2/records/index\" \\\n  -H \"Authorization: Bearer YOUR_ACCESS_TOKEN\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"page\": 1,\n    \"limit\": 25,\n    \"mainObject\": \"person\"\n  }'\n","lang":"bash"},"children":[]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["The property values are identical across contexts. What changes is which object"," ","schema, views, and permissions apply for that request."]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"alternative-morph-per-record","__idx":9},"children":["Alternative: morph per record"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["If only some people should also be contacts or candidates, skip ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["extendsTo"]}," and"," ","morph records individually:"]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"bash","header":{"controls":{"copy":{}}},"source":"curl -X POST \"https://your-company.caraer.com/api/v2/records/RECORD_UUID/morph\" \\\n  -H \"Authorization: Bearer YOUR_ACCESS_TOKEN\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"objects\": [\n      { \"name\": \"contact\" },\n      { \"name\": \"candidate\" }\n    ]\n  }'\n","lang":"bash"},"children":[]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["The morph endpoint sets morph objects for that record. It does not change the"," ","record's primary object."]},{"$$mdtype":"Tag","name":"hr","attributes":{},"children":[]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"morph-api-reference","__idx":10},"children":["Morph API reference"]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"morph-a-record","__idx":11},"children":["Morph a record"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["POST /api/v2/records/{uuid}/morph"]}]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Request body:"]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"json","header":{"controls":{"copy":{}}},"source":"{\n  \"objects\": [\n    { \"name\": \"contact\" },\n    { \"name\": \"candidate\" }\n  ]\n}\n","lang":"json"},"children":[]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Use this to:"]},{"$$mdtype":"Tag","name":"ul","attributes":{},"children":[{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Add visibility in additional object contexts"]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Remove visibility by sending a smaller object list (objects not in the list"," ","are removed from the morph set)"]}]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Query params:"]},{"$$mdtype":"Tag","name":"ul","attributes":{},"children":[{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["recordReturnFormat"]}," — ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["LEGACY"]},", ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["USER_FRIENDLY"]},", or ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["EXPANDED"]}]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["parse"]}," — parse formatted values before returning the record"]}]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"sync-morph-objects-for-an-object","__idx":12},"children":["Sync morph objects for an object"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["POST /api/v2/objects/{uuid}/syncMorphObjects"]}]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Use after changing an object's ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["extendsTo"]}," configuration so existing records"," ","pick up the new default morph targets."]},{"$$mdtype":"Tag","name":"hr","attributes":{},"children":[]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"validation-and-constraints","__idx":13},"children":["Validation and constraints"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Caraer enforces these morph rules:"]},{"$$mdtype":"Tag","name":"ul","attributes":{},"children":[{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Primary object cannot appear in morph objects."]}," If ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["person"]}," is primary,"," ","do not include ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["person"]}," in the morph list."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["At most one Page trait"]}," across primary + morph objects."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["At most one User trait"]}," across primary + morph objects."]}]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["These limits prevent conflicting trait behavior when one record spans multiple"," ","object types."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Unique-property checks consider ",{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["all objects"]}," on the record. If ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["email"]}," is"," ","unique on both ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["person"]}," and ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["contact"]},", duplicate detection spans the combined"," ","object context."]},{"$$mdtype":"Tag","name":"hr","attributes":{},"children":[]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"design-guidelines","__idx":14},"children":["Design guidelines"]},{"$$mdtype":"Tag","name":"ul","attributes":{},"children":[{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Use a ",{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["base primary object"]}," (",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["person"]},") when multiple specialized types"," ","share the same identity data."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Use ",{"$$mdtype":"Tag","name":"strong","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["extendsTo"]}]}," for defaults that should apply to every new record of"," ","that type."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Use the ",{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["morph endpoint"]}," for exceptions and lifecycle changes (for example,"," ","promoting one person to candidate only)."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Keep shared property names aligned across morphed objects so values stay"," ","consistent in every view."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["After schema changes to ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["extendsTo"]},", run ",{"$$mdtype":"Tag","name":"strong","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["syncMorphObjects"]}]},"."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Query with the object context you need via ",{"$$mdtype":"Tag","name":"strong","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["mainObject"]}]},"; do not assume"," ","records only exist under their primary object."]}]},{"$$mdtype":"Tag","name":"hr","attributes":{},"children":[]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"common-pitfalls","__idx":15},"children":["Common pitfalls"]},{"$$mdtype":"Tag","name":"div","attributes":{"className":"md-table-wrapper"},"children":[{"$$mdtype":"Tag","name":"table","attributes":{"className":"md"},"children":[{"$$mdtype":"Tag","name":"thead","attributes":{},"children":[{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"th","attributes":{"data-label":"Pitfall"},"children":["Pitfall"]},{"$$mdtype":"Tag","name":"th","attributes":{"data-label":"What happens"},"children":["What happens"]},{"$$mdtype":"Tag","name":"th","attributes":{"data-label":"Fix"},"children":["Fix"]}]}]},{"$$mdtype":"Tag","name":"tbody","attributes":{},"children":[{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Expecting morph to change primary object"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Primary object stays the same"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Create the record under the intended primary type, or accept that ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["primary_object"]}," reflects creation context"]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Forgetting ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["extendsTo"]}," only affects new saves + sync"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Existing records keep old morph set"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Call ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["syncMorphObjects"]}," after schema changes"]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Including primary object in morph request"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Validation error"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Remove the primary object from the morph list"]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Different property sets per object"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Record may lack expected fields in one view"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Attach required properties to every object context users will open"]}]}]}]}]},{"$$mdtype":"Tag","name":"hr","attributes":{},"children":[]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"related-reading","__idx":16},"children":["Related reading"]},{"$$mdtype":"Tag","name":"ul","attributes":{},"children":[{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"a","attributes":{"href":"/blog/2026-03-25-caraer-data-model-object-property-relation"},"children":["Caraer data model: Object, Property, Relation"]}]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"a","attributes":{"href":"/blog/2026-06-07-create-and-share-properties"},"children":["Create and share properties"]}]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"a","attributes":{"href":"/blog/2026-03-25-save-records"},"children":["Save records"]}]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"a","attributes":{"href":"/blog/2026-03-25-fetch-records-and-filtering-guide"},"children":["Fetch records and filtering guide"]}]}]}]},"headings":[{"value":"Why morphing exists","id":"why-morphing-exists","depth":2},{"value":"The mental model","id":"the-mental-model","depth":2},{"value":"How morphing works","id":"how-morphing-works","depth":2},{"value":"Object-level configuration: extendsTo","id":"object-level-configuration-extendsto","depth":2},{"value":"Syncing existing records after schema changes","id":"syncing-existing-records-after-schema-changes","depth":3},{"value":"Example: person → contact and candidate","id":"example-person--contact-and-candidate","depth":2},{"value":"Scenario","id":"scenario","depth":3},{"value":"Recommended setup","id":"recommended-setup","depth":3},{"value":"Querying the same record in different contexts","id":"querying-the-same-record-in-different-contexts","depth":3},{"value":"Alternative: morph per record","id":"alternative-morph-per-record","depth":3},{"value":"Morph API reference","id":"morph-api-reference","depth":2},{"value":"Morph a record","id":"morph-a-record","depth":3},{"value":"Sync morph objects for an object","id":"sync-morph-objects-for-an-object","depth":3},{"value":"Validation and constraints","id":"validation-and-constraints","depth":2},{"value":"Design guidelines","id":"design-guidelines","depth":2},{"value":"Common pitfalls","id":"common-pitfalls","depth":2},{"value":"Related reading","id":"related-reading","depth":2}],"frontmatter":{"title":"Object morph explained: primary object, morph objects, and person → contact/candidate","authors":["sem_tadema"],"tags":["documentation","api","schema"],"template":"../@theme/Templates/BlogWithAuthors","seo":{"title":"Why morphing exists"}},"lastModified":"2026-06-07T14:08:38.000Z","pagePropGetterError":{"message":"","name":""}},"slug":"/blog/2026-06-07-object-morph-explained","userData":{"isAuthenticated":false,"teams":["anonymous"]},"isPublic":true}