> ## Documentation Index
> Fetch the complete documentation index at: https://developers.hubspot.com/docs/llms.txt
> Use this file to discover all available pages before exploring further.

---
id: 76dbadaa-60e1-4f74-8a36-3498ef6a31e7
---

# Course object definition

> Information about the Course object, including identifiers, feature support, and default properties.

export const objectMetadata = {
  contact: {
    objectId: "0-1",
    displayName: "contact",
    pluralName: "contacts",
    linkUrl: "/api-reference/latest/crm/objects/contacts/guide"
  },
  company: {
    objectId: "0-2",
    displayName: "company",
    pluralName: "companies",
    linkUrl: "/api-reference/latest/crm/objects/companies/guide"
  },
  campaign: {
    objectId: "0-35",
    displayName: "campaign",
    pluralName: "campaigns",
    linkUrl: "/api-reference/marketing-campaigns-public-api-v3/guide"
  },
  deal: {
    objectId: "0-3",
    displayName: "deal",
    pluralName: "deals",
    linkUrl: "/api-reference/latest/crm/objects/deals/guide"
  },
  goal_target: {
    objectId: "0-74",
    displayName: "goal target",
    pluralName: "goal targets",
    linkUrl: "/api-reference/latest/crm/objects/goal-targets/guide"
  },
  ticket: {
    objectId: "0-5",
    displayName: "ticket",
    pluralName: "tickets",
    linkUrl: "/api-reference/latest/crm/objects/tickets/guide"
  },
  lead: {
    objectId: "0-136",
    displayName: "lead",
    pluralName: "leads",
    linkUrl: "/api-reference/latest/crm/objects/leads/guide"
  },
  appointment: {
    objectId: "0-421",
    displayName: "appointment",
    pluralName: "appointments"
  },
  course: {
    objectId: "0-410",
    displayName: "course",
    pluralName: "courses"
  },
  listing: {
    objectId: "0-420",
    displayName: "listing",
    pluralName: "listings"
  },
  service: {
    objectId: "0-162",
    displayName: "service",
    pluralName: "services"
  },
  call: {
    objectId: "0-48",
    displayName: "call",
    pluralName: "calls",
    linkUrl: "/api-reference/latest/crm/activities/calls/guide"
  },
  email: {
    objectId: "0-49",
    displayName: "email",
    pluralName: "emails",
    linkUrl: "/api-reference/latest/crm/activities/emails/guide"
  },
  meeting: {
    objectId: "0-47",
    displayName: "meeting",
    pluralName: "meetings",
    linkUrl: "/api-reference/latest/crm/activities/meetings/guide"
  },
  note: {
    objectId: "0-46",
    displayName: "note",
    pluralName: "notes",
    linkUrl: "/api-reference/latest/crm/activities/notes/guide"
  },
  task: {
    objectId: "0-27",
    displayName: "task",
    pluralName: "tasks",
    linkUrl: "/api-reference/latest/crm/activities/tasks/guide"
  },
  communication: {
    objectId: "0-18",
    displayName: "communication",
    pluralName: "communications",
    linkUrl: "/api-reference/latest/crm/objects/communications/guide"
  },
  postal_mail: {
    objectId: "0-116",
    displayName: "postal mail",
    pluralName: "postal mail",
    linkUrl: "/api-reference/latest/crm/activities/postal-mail/guide"
  },
  cart: {
    objectId: "0-142",
    displayName: "cart",
    pluralName: "carts",
    linkUrl: "/api-reference/latest/crm/objects/carts/guide"
  },
  order: {
    objectId: "0-123",
    displayName: "order",
    pluralName: "orders",
    linkUrl: "/api-reference/latest/crm/objects/orders/guide"
  },
  invoice: {
    objectId: "0-53",
    displayName: "invoice",
    pluralName: "invoices",
    linkUrl: "/api-reference/latest/crm/objects/invoices/guide"
  },
  commerce_payment: {
    objectId: "0-101",
    displayName: "payment",
    pluralName: "payments",
    linkUrl: "/api-reference/latest/crm/objects/commerce-payments/guide"
  },
  payment_link: {
    objectId: "0-118",
    displayName: "payment link",
    pluralName: "payment links",
    linkUrl: "/api-reference/latest/crm/objects/commerce-payments/guide"
  },
  subscription: {
    objectId: "0-69",
    displayName: "subscription",
    pluralName: "subscriptions",
    linkUrl: "/api-reference/latest/crm/objects/commerce-subscriptions/guide"
  },
  feedback_submission: {
    objectId: "0-19",
    displayName: "feedback submission",
    pluralName: "feedback submissions",
    linkUrl: "/api-reference/latest/crm/objects/feedback-submissions/guide"
  },
  project: {
    objectId: "0-970",
    displayName: "project",
    pluralName: "projects",
    linkUrl: "/api-reference/latest/crm/objects/projects/guide"
  },
  quote: {
    objectId: "0-14",
    displayName: "quote",
    pluralName: "quotes",
    linkUrl: "/api-reference/latest/crm/objects/quotes/guide"
  },
  line_item: {
    objectId: "0-8",
    displayName: "line item",
    pluralName: "line items",
    linkUrl: "/api-reference/latest/crm/objects/line-items/guide"
  },
  discount: {
    objectId: "0-84",
    displayName: "discount",
    pluralName: "discounts",
    linkUrl: "/api-reference/latest/crm/objects/discounts/guide"
  },
  fee: {
    objectId: "0-85",
    displayName: "fee",
    pluralName: "fees",
    linkUrl: "/api-reference/latest/crm/objects/fees/guide"
  },
  tax: {
    objectId: "0-86",
    displayName: "tax",
    pluralName: "taxes",
    linkUrl: "/api-reference/latest/crm/objects/taxes/guide"
  },
  deal_split: {
    objectId: "0-72",
    displayName: "deal split",
    pluralName: "deal splits",
    linkUrl: "/api-reference/latest/crm/objects/deal-splits/guide"
  },
  conversation: {
    objectId: "0-11",
    displayName: "conversation",
    pluralName: "conversations",
    linkUrl: "/api-reference/latest/conversations/guide"
  },
  discount_code: {
    objectId: "0-110",
    displayName: "discount code",
    pluralName: "discount codes",
    linkUrl: "/api-reference/latest/crm/objects/discounts/guide"
  },
  quote_template: {
    objectId: "0-64",
    displayName: "quote template",
    pluralName: "quote templates",
    linkUrl: "/api-reference/latest/crm/objects/quotes/guide"
  },
  marketing_event: {
    objectId: "0-54",
    displayName: "marketing event",
    pluralName: "marketing events",
    linkUrl: "/api-reference/latest/marketing/marketing-events/guide"
  },
  product: {
    objectId: "0-7",
    displayName: "product",
    pluralName: "products",
    linkUrl: "/api-reference/latest/crm/objects/products/guide"
  },
  user: {
    objectId: "0-115",
    displayName: "user",
    pluralName: "users",
    linkUrl: "/api-reference/latest/crm/objects/users/guide"
  }
};

export const associationsData = {
  "associations": [{
    "id": "appointment_appointment",
    "fromObject": "appointment",
    "toObject": "appointment",
    "forwardId": 950,
    "forwardCardinality": "ONE_TO_MANY",
    "inverseId": 950,
    "inverseCardinality": "ONE_TO_MANY",
    "labeledTypes": []
  }, {
    "id": "call_appointment",
    "fromObject": "call",
    "toObject": "appointment",
    "forwardId": 913,
    "forwardCardinality": "ONE_TO_MANY",
    "inverseId": 912,
    "inverseCardinality": "ONE_TO_MANY",
    "labeledTypes": []
  }, {
    "id": "call_call",
    "fromObject": "call",
    "toObject": "call",
    "forwardId": 1032,
    "forwardCardinality": "ONE_TO_MANY",
    "inverseId": 1032,
    "inverseCardinality": "ONE_TO_MANY",
    "labeledTypes": []
  }, {
    "id": "call_cart",
    "fromObject": "call",
    "toObject": "cart",
    "forwardId": 775,
    "forwardCardinality": "ONE_TO_MANY",
    "inverseId": 774,
    "inverseCardinality": "ONE_TO_MANY",
    "labeledTypes": []
  }, {
    "id": "call_course",
    "fromObject": "call",
    "toObject": "course",
    "forwardId": 867,
    "forwardCardinality": "ONE_TO_MANY",
    "inverseId": 866,
    "inverseCardinality": "ONE_TO_MANY",
    "labeledTypes": []
  }, {
    "id": "call_lead",
    "fromObject": "call",
    "toObject": "lead",
    "forwardId": 597,
    "forwardCardinality": "ONE_TO_MANY",
    "inverseId": 596,
    "inverseCardinality": "ONE_TO_MANY",
    "labeledTypes": []
  }, {
    "id": "call_listing",
    "fromObject": "call",
    "toObject": "listing",
    "forwardId": 891,
    "forwardCardinality": "ONE_TO_MANY",
    "inverseId": 890,
    "inverseCardinality": "ONE_TO_MANY",
    "labeledTypes": []
  }, {
    "id": "call_marketing_event",
    "fromObject": "call",
    "toObject": "marketing_event",
    "forwardId": 1229,
    "forwardCardinality": "ONE_TO_MANY",
    "inverseId": 1228,
    "inverseCardinality": "ONE_TO_MANY",
    "labeledTypes": []
  }, {
    "id": "call_order",
    "fromObject": "call",
    "toObject": "order",
    "forwardId": 773,
    "forwardCardinality": "ONE_TO_MANY",
    "inverseId": 772,
    "inverseCardinality": "ONE_TO_MANY",
    "labeledTypes": []
  }, {
    "id": "call_project",
    "fromObject": "call",
    "toObject": "project",
    "forwardId": 1258,
    "forwardCardinality": "ONE_TO_MANY",
    "inverseId": 1257,
    "inverseCardinality": "ONE_TO_MANY",
    "labeledTypes": []
  }, {
    "id": "call_service",
    "fromObject": "call",
    "toObject": "service",
    "forwardId": 841,
    "forwardCardinality": "ONE_TO_MANY",
    "inverseId": 840,
    "inverseCardinality": "ONE_TO_MANY",
    "labeledTypes": []
  }, {
    "id": "campaign_call",
    "fromObject": "campaign",
    "toObject": "call",
    "forwardId": 954,
    "forwardCardinality": "ONE_TO_MANY",
    "inverseId": 955,
    "inverseCardinality": "ONE_TO_ONE",
    "labeledTypes": []
  }, {
    "id": "campaign_email",
    "fromObject": "campaign",
    "toObject": "email",
    "forwardId": 958,
    "forwardCardinality": "ONE_TO_MANY",
    "inverseId": 959,
    "inverseCardinality": "ONE_TO_ONE",
    "labeledTypes": []
  }, {
    "id": "campaign_goal_target",
    "fromObject": "campaign",
    "toObject": "goal_target",
    "forwardId": 808,
    "forwardCardinality": "ONE_TO_MANY",
    "inverseId": 809,
    "inverseCardinality": "ONE_TO_MANY",
    "labeledTypes": []
  }, {
    "id": "campaign_marketing_event",
    "fromObject": "campaign",
    "toObject": "marketing_event",
    "forwardId": 491,
    "forwardCardinality": "ONE_TO_MANY",
    "inverseId": 492,
    "inverseCardinality": "ONE_TO_MANY",
    "labeledTypes": []
  }, {
    "id": "campaign_meeting",
    "fromObject": "campaign",
    "toObject": "meeting",
    "forwardId": 956,
    "forwardCardinality": "ONE_TO_MANY",
    "inverseId": 957,
    "inverseCardinality": "ONE_TO_MANY",
    "labeledTypes": []
  }, {
    "id": "campaign_payment_link",
    "fromObject": "campaign",
    "toObject": "payment_link",
    "forwardId": 1329,
    "forwardCardinality": "ONE_TO_MANY",
    "inverseId": 1328,
    "inverseCardinality": "ONE_TO_MANY",
    "labeledTypes": []
  }, {
    "id": "campaign_project",
    "fromObject": "campaign",
    "toObject": "project",
    "forwardId": 1350,
    "forwardCardinality": "ONE_TO_MANY",
    "inverseId": 1351,
    "inverseCardinality": "ONE_TO_MANY",
    "labeledTypes": []
  }, {
    "id": "commerce_payment_order",
    "fromObject": "commerce_payment",
    "toObject": "order",
    "forwardId": 524,
    "forwardCardinality": "ONE_TO_ONE",
    "inverseId": 523,
    "inverseCardinality": "ONE_TO_MANY",
    "labeledTypes": []
  }, {
    "id": "commerce_payment_payment_link",
    "fromObject": "commerce_payment",
    "toObject": "payment_link",
    "forwardId": 476,
    "forwardCardinality": "ONE_TO_ONE",
    "inverseId": 475,
    "inverseCardinality": "ONE_TO_MANY",
    "labeledTypes": []
  }, {
    "id": "communication_appointment",
    "fromObject": "communication",
    "toObject": "appointment",
    "forwardId": 925,
    "forwardCardinality": "ONE_TO_MANY",
    "inverseId": 924,
    "inverseCardinality": "ONE_TO_MANY",
    "labeledTypes": []
  }, {
    "id": "communication_cart",
    "fromObject": "communication",
    "toObject": "cart",
    "forwardId": 787,
    "forwardCardinality": "ONE_TO_MANY",
    "inverseId": 786,
    "inverseCardinality": "ONE_TO_MANY",
    "labeledTypes": []
  }, {
    "id": "communication_course",
    "fromObject": "communication",
    "toObject": "course",
    "forwardId": 879,
    "forwardCardinality": "ONE_TO_MANY",
    "inverseId": 878,
    "inverseCardinality": "ONE_TO_MANY",
    "labeledTypes": []
  }, {
    "id": "communication_lead",
    "fromObject": "communication",
    "toObject": "lead",
    "forwardId": 603,
    "forwardCardinality": "ONE_TO_MANY",
    "inverseId": 602,
    "inverseCardinality": "ONE_TO_MANY",
    "labeledTypes": []
  }, {
    "id": "communication_listing",
    "fromObject": "communication",
    "toObject": "listing",
    "forwardId": 903,
    "forwardCardinality": "ONE_TO_MANY",
    "inverseId": 902,
    "inverseCardinality": "ONE_TO_MANY",
    "labeledTypes": []
  }, {
    "id": "communication_marketing_event",
    "fromObject": "communication",
    "toObject": "marketing_event",
    "forwardId": 1235,
    "forwardCardinality": "ONE_TO_MANY",
    "inverseId": 1234,
    "inverseCardinality": "ONE_TO_MANY",
    "labeledTypes": []
  }, {
    "id": "communication_order",
    "fromObject": "communication",
    "toObject": "order",
    "forwardId": 785,
    "forwardCardinality": "ONE_TO_MANY",
    "inverseId": 784,
    "inverseCardinality": "ONE_TO_MANY",
    "labeledTypes": []
  }, {
    "id": "communication_project",
    "fromObject": "communication",
    "toObject": "project",
    "forwardId": 1282,
    "forwardCardinality": "ONE_TO_MANY",
    "inverseId": 1281,
    "inverseCardinality": "ONE_TO_MANY",
    "labeledTypes": []
  }, {
    "id": "communication_service",
    "fromObject": "communication",
    "toObject": "service",
    "forwardId": 847,
    "forwardCardinality": "ONE_TO_MANY",
    "inverseId": 846,
    "inverseCardinality": "ONE_TO_MANY",
    "labeledTypes": []
  }, {
    "id": "company_appointment",
    "fromObject": "company",
    "toObject": "appointment",
    "forwardId": 909,
    "forwardCardinality": "ONE_TO_MANY",
    "inverseId": 908,
    "inverseCardinality": "ONE_TO_MANY",
    "labeledTypes": []
  }, {
    "id": "company_call",
    "fromObject": "company",
    "toObject": "call",
    "forwardId": 181,
    "forwardCardinality": "ONE_TO_MANY",
    "inverseId": 182,
    "inverseCardinality": "ONE_TO_MANY",
    "labeledTypes": []
  }, {
    "id": "company_cart",
    "fromObject": "company",
    "toObject": "cart",
    "forwardId": 937,
    "forwardCardinality": "ONE_TO_MANY",
    "inverseId": 936,
    "inverseCardinality": "ONE_TO_MANY",
    "labeledTypes": [{
      "label": "Primary",
      "forwardId": 761,
      "forwardCardinality": "ONE_TO_MANY",
      "inverseId": 760,
      "inverseCardinality": "ONE_TO_ONE",
      "isPrimary": true
    }]
  }, {
    "id": "company_commerce_payment",
    "fromObject": "company",
    "toObject": "commerce_payment",
    "forwardId": 390,
    "forwardCardinality": "ONE_TO_MANY",
    "inverseId": 389,
    "inverseCardinality": "ONE_TO_ONE",
    "labeledTypes": []
  }, {
    "id": "company_communication",
    "fromObject": "company",
    "toObject": "communication",
    "forwardId": 88,
    "forwardCardinality": "ONE_TO_MANY",
    "inverseId": 87,
    "inverseCardinality": "ONE_TO_MANY",
    "labeledTypes": []
  }, {
    "id": "company_company",
    "fromObject": "company",
    "toObject": "company",
    "forwardId": 450,
    "forwardCardinality": "ONE_TO_MANY",
    "inverseId": 450,
    "inverseCardinality": "ONE_TO_MANY",
    "labeledTypes": [{
      "label": "Child",
      "inverseLabel": "Parent",
      "forwardId": 13,
      "forwardCardinality": "ONE_TO_MANY",
      "inverseId": 14,
      "inverseCardinality": "ONE_TO_ONE",
      "isPrimary": false
    }]
  }, {
    "id": "company_course",
    "fromObject": "company",
    "toObject": "course",
    "forwardId": 939,
    "forwardCardinality": "ONE_TO_MANY",
    "inverseId": 938,
    "inverseCardinality": "ONE_TO_MANY",
    "labeledTypes": []
  }, {
    "id": "company_deal",
    "fromObject": "company",
    "toObject": "deal",
    "forwardId": 342,
    "forwardCardinality": "ONE_TO_MANY",
    "inverseId": 341,
    "inverseCardinality": "ONE_TO_MANY",
    "labeledTypes": [{
      "label": "Primary",
      "forwardId": 6,
      "forwardCardinality": "ONE_TO_MANY",
      "inverseId": 5,
      "inverseCardinality": "ONE_TO_ONE",
      "isPrimary": true
    }]
  }, {
    "id": "company_email",
    "fromObject": "company",
    "toObject": "email",
    "forwardId": 185,
    "forwardCardinality": "ONE_TO_MANY",
    "inverseId": 186,
    "inverseCardinality": "ONE_TO_MANY",
    "labeledTypes": []
  }, {
    "id": "company_feedback_submission",
    "fromObject": "company",
    "toObject": "feedback_submission",
    "forwardId": 929,
    "forwardCardinality": "ONE_TO_MANY",
    "inverseId": 928,
    "inverseCardinality": "ONE_TO_ONE",
    "labeledTypes": []
  }, {
    "id": "company_invoice",
    "fromObject": "company",
    "toObject": "invoice",
    "forwardId": 180,
    "forwardCardinality": "ONE_TO_MANY",
    "inverseId": 179,
    "inverseCardinality": "ONE_TO_MANY",
    "labeledTypes": []
  }, {
    "id": "company_lead",
    "fromObject": "company",
    "toObject": "lead",
    "forwardId": 611,
    "forwardCardinality": "ONE_TO_MANY",
    "inverseId": 610,
    "inverseCardinality": "ONE_TO_MANY",
    "labeledTypes": [{
      "label": "Primary",
      "forwardId": 581,
      "forwardCardinality": "ONE_TO_MANY",
      "inverseId": 580,
      "inverseCardinality": "ONE_TO_ONE",
      "isPrimary": true
    }]
  }, {
    "id": "company_listing",
    "fromObject": "company",
    "toObject": "listing",
    "forwardId": 885,
    "forwardCardinality": "ONE_TO_MANY",
    "inverseId": 884,
    "inverseCardinality": "ONE_TO_MANY",
    "labeledTypes": []
  }, {
    "id": "company_marketing_event",
    "fromObject": "company",
    "toObject": "marketing_event",
    "forwardId": 1387,
    "forwardCardinality": "ONE_TO_MANY",
    "inverseId": 1386,
    "inverseCardinality": "ONE_TO_MANY",
    "labeledTypes": []
  }, {
    "id": "company_meeting",
    "fromObject": "company",
    "toObject": "meeting",
    "forwardId": 187,
    "forwardCardinality": "ONE_TO_MANY",
    "inverseId": 188,
    "inverseCardinality": "ONE_TO_MANY",
    "labeledTypes": []
  }, {
    "id": "company_note",
    "fromObject": "company",
    "toObject": "note",
    "forwardId": 189,
    "forwardCardinality": "ONE_TO_MANY",
    "inverseId": 190,
    "inverseCardinality": "ONE_TO_MANY",
    "labeledTypes": []
  }, {
    "id": "company_order",
    "fromObject": "company",
    "toObject": "order",
    "forwardId": 935,
    "forwardCardinality": "ONE_TO_MANY",
    "inverseId": 934,
    "inverseCardinality": "ONE_TO_MANY",
    "labeledTypes": [{
      "label": "Primary",
      "forwardId": 510,
      "forwardCardinality": "ONE_TO_MANY",
      "inverseId": 509,
      "inverseCardinality": "ONE_TO_ONE",
      "isPrimary": true
    }, {
      "label": "Billing",
      "forwardId": 2693,
      "forwardCardinality": "ONE_TO_MANY",
      "inverseId": 2692,
      "inverseCardinality": "ONE_TO_ONE",
      "isPrimary": false
    }]
  }, {
    "id": "company_payment_link",
    "fromObject": "company",
    "toObject": "payment_link",
    "forwardId": 472,
    "forwardCardinality": "ONE_TO_MANY",
    "inverseId": 471,
    "inverseCardinality": "ONE_TO_MANY",
    "labeledTypes": []
  }, {
    "id": "company_postal_mail",
    "fromObject": "company",
    "toObject": "postal_mail",
    "forwardId": 460,
    "forwardCardinality": "ONE_TO_MANY",
    "inverseId": 459,
    "inverseCardinality": "ONE_TO_MANY",
    "labeledTypes": []
  }, {
    "id": "company_project",
    "fromObject": "company",
    "toObject": "project",
    "forwardId": 1237,
    "forwardCardinality": "ONE_TO_MANY",
    "inverseId": 1236,
    "inverseCardinality": "ONE_TO_MANY",
    "labeledTypes": [{
      "label": "Primary",
      "forwardId": 1300,
      "forwardCardinality": "ONE_TO_MANY",
      "inverseId": 1299,
      "inverseCardinality": "ONE_TO_ONE",
      "isPrimary": true
    }]
  }, {
    "id": "company_quote",
    "fromObject": "company",
    "toObject": "quote",
    "forwardId": 72,
    "forwardCardinality": "ONE_TO_MANY",
    "inverseId": 71,
    "inverseCardinality": "ONE_TO_ONE",
    "labeledTypes": [{
      "label": "Billing",
      "forwardId": 1335,
      "forwardCardinality": "ONE_TO_MANY",
      "inverseId": 1334,
      "inverseCardinality": "ONE_TO_ONE",
      "isPrimary": false
    }]
  }, {
    "id": "company_service",
    "fromObject": "company",
    "toObject": "service",
    "forwardId": 793,
    "forwardCardinality": "ONE_TO_MANY",
    "inverseId": 792,
    "inverseCardinality": "ONE_TO_MANY",
    "labeledTypes": []
  }, {
    "id": "company_subscription",
    "fromObject": "company",
    "toObject": "subscription",
    "forwardId": 298,
    "forwardCardinality": "ONE_TO_MANY",
    "inverseId": 297,
    "inverseCardinality": "ONE_TO_ONE",
    "labeledTypes": []
  }, {
    "id": "company_task",
    "fromObject": "company",
    "toObject": "task",
    "forwardId": 191,
    "forwardCardinality": "ONE_TO_MANY",
    "inverseId": 192,
    "inverseCardinality": "ONE_TO_MANY",
    "labeledTypes": []
  }, {
    "id": "company_ticket",
    "fromObject": "company",
    "toObject": "ticket",
    "forwardId": 340,
    "forwardCardinality": "ONE_TO_MANY",
    "inverseId": 339,
    "inverseCardinality": "ONE_TO_MANY",
    "labeledTypes": [{
      "label": "Primary",
      "forwardId": 25,
      "forwardCardinality": "ONE_TO_MANY",
      "inverseId": 26,
      "inverseCardinality": "ONE_TO_ONE",
      "isPrimary": true
    }]
  }, {
    "id": "contact_appointment",
    "fromObject": "contact",
    "toObject": "appointment",
    "forwardId": 907,
    "forwardCardinality": "ONE_TO_MANY",
    "inverseId": 906,
    "inverseCardinality": "ONE_TO_MANY",
    "labeledTypes": []
  }, {
    "id": "contact_call",
    "fromObject": "contact",
    "toObject": "call",
    "forwardId": 193,
    "forwardCardinality": "ONE_TO_MANY",
    "inverseId": 194,
    "inverseCardinality": "ONE_TO_MANY",
    "labeledTypes": []
  }, {
    "id": "contact_campaign",
    "fromObject": "contact",
    "toObject": "campaign",
    "forwardId": 1184,
    "forwardCardinality": "ONE_TO_MANY",
    "inverseId": 1183,
    "inverseCardinality": "ONE_TO_MANY",
    "labeledTypes": []
  }, {
    "id": "contact_cart",
    "fromObject": "contact",
    "toObject": "cart",
    "forwardId": 587,
    "forwardCardinality": "ONE_TO_MANY",
    "inverseId": 586,
    "inverseCardinality": "ONE_TO_MANY",
    "labeledTypes": []
  }, {
    "id": "contact_commerce_payment",
    "fromObject": "contact",
    "toObject": "commerce_payment",
    "forwardId": 388,
    "forwardCardinality": "ONE_TO_MANY",
    "inverseId": 387,
    "inverseCardinality": "ONE_TO_ONE",
    "labeledTypes": []
  }, {
    "id": "contact_communication",
    "fromObject": "contact",
    "toObject": "communication",
    "forwardId": 82,
    "forwardCardinality": "ONE_TO_MANY",
    "inverseId": 81,
    "inverseCardinality": "ONE_TO_MANY",
    "labeledTypes": []
  }, {
    "id": "contact_company",
    "fromObject": "contact",
    "toObject": "company",
    "forwardId": 279,
    "forwardCardinality": "ONE_TO_MANY",
    "inverseId": 280,
    "inverseCardinality": "ONE_TO_MANY",
    "labeledTypes": [{
      "label": "Primary",
      "forwardId": 1,
      "forwardCardinality": "ONE_TO_ONE",
      "inverseId": 2,
      "inverseCardinality": "ONE_TO_MANY",
      "isPrimary": true
    }, {
      "label": "Billing",
      "forwardId": 931,
      "forwardCardinality": "ONE_TO_MANY",
      "inverseId": 930,
      "inverseCardinality": "ONE_TO_ONE",
      "isPrimary": false
    }]
  }, {
    "id": "contact_contact",
    "fromObject": "contact",
    "toObject": "contact",
    "forwardId": 449,
    "forwardCardinality": "ONE_TO_MANY",
    "inverseId": 449,
    "inverseCardinality": "ONE_TO_MANY",
    "labeledTypes": []
  }, {
    "id": "contact_conversation",
    "fromObject": "contact",
    "toObject": "conversation",
    "forwardId": 174,
    "forwardCardinality": "ONE_TO_MANY",
    "inverseId": 173,
    "inverseCardinality": "ONE_TO_MANY",
    "labeledTypes": []
  }, {
    "id": "contact_course",
    "fromObject": "contact",
    "toObject": "course",
    "forwardId": 861,
    "forwardCardinality": "ONE_TO_MANY",
    "inverseId": 860,
    "inverseCardinality": "ONE_TO_MANY",
    "labeledTypes": []
  }, {
    "id": "contact_deal",
    "fromObject": "contact",
    "toObject": "deal",
    "forwardId": 4,
    "forwardCardinality": "ONE_TO_MANY",
    "inverseId": 3,
    "inverseCardinality": "ONE_TO_MANY",
    "labeledTypes": []
  }, {
    "id": "contact_email",
    "fromObject": "contact",
    "toObject": "email",
    "forwardId": 197,
    "forwardCardinality": "ONE_TO_MANY",
    "inverseId": 198,
    "inverseCardinality": "ONE_TO_MANY",
    "labeledTypes": []
  }, {
    "id": "contact_feedback_submission",
    "fromObject": "contact",
    "toObject": "feedback_submission",
    "forwardId": 97,
    "forwardCardinality": "ONE_TO_MANY",
    "inverseId": 98,
    "inverseCardinality": "ONE_TO_ONE",
    "labeledTypes": []
  }, {
    "id": "contact_invoice",
    "fromObject": "contact",
    "toObject": "invoice",
    "forwardId": 178,
    "forwardCardinality": "ONE_TO_MANY",
    "inverseId": 177,
    "inverseCardinality": "ONE_TO_MANY",
    "labeledTypes": []
  }, {
    "id": "contact_lead",
    "fromObject": "contact",
    "toObject": "lead",
    "forwardId": 609,
    "forwardCardinality": "ONE_TO_MANY",
    "inverseId": 608,
    "inverseCardinality": "ONE_TO_MANY",
    "labeledTypes": [{
      "label": "Primary",
      "forwardId": 579,
      "forwardCardinality": "ONE_TO_MANY",
      "inverseId": 578,
      "inverseCardinality": "ONE_TO_ONE",
      "isPrimary": true
    }]
  }, {
    "id": "contact_listing",
    "fromObject": "contact",
    "toObject": "listing",
    "forwardId": 883,
    "forwardCardinality": "ONE_TO_MANY",
    "inverseId": 882,
    "inverseCardinality": "ONE_TO_MANY",
    "labeledTypes": []
  }, {
    "id": "contact_marketing_event",
    "fromObject": "contact",
    "toObject": "marketing_event",
    "forwardId": 1375,
    "forwardCardinality": "ONE_TO_MANY",
    "inverseId": 1374,
    "inverseCardinality": "ONE_TO_MANY",
    "labeledTypes": [{
      "label": "Attendee",
      "forwardId": 1381,
      "forwardCardinality": "ONE_TO_MANY",
      "inverseId": 1380,
      "inverseCardinality": "ONE_TO_MANY",
      "isPrimary": false
    }, {
      "label": "Cancelled registrant",
      "forwardId": 1379,
      "forwardCardinality": "ONE_TO_MANY",
      "inverseId": 1378,
      "inverseCardinality": "ONE_TO_MANY",
      "isPrimary": false
    }, {
      "label": "Registrant",
      "forwardId": 1377,
      "forwardCardinality": "ONE_TO_MANY",
      "inverseId": 1376,
      "inverseCardinality": "ONE_TO_MANY",
      "isPrimary": false
    }]
  }, {
    "id": "contact_meeting",
    "fromObject": "contact",
    "toObject": "meeting",
    "forwardId": 199,
    "forwardCardinality": "ONE_TO_MANY",
    "inverseId": 200,
    "inverseCardinality": "ONE_TO_MANY",
    "labeledTypes": []
  }, {
    "id": "contact_note",
    "fromObject": "contact",
    "toObject": "note",
    "forwardId": 201,
    "forwardCardinality": "ONE_TO_MANY",
    "inverseId": 202,
    "inverseCardinality": "ONE_TO_MANY",
    "labeledTypes": []
  }, {
    "id": "contact_order",
    "fromObject": "contact",
    "toObject": "order",
    "forwardId": 508,
    "forwardCardinality": "ONE_TO_MANY",
    "inverseId": 507,
    "inverseCardinality": "ONE_TO_MANY",
    "labeledTypes": []
  }, {
    "id": "contact_payment_link",
    "fromObject": "contact",
    "toObject": "payment_link",
    "forwardId": 470,
    "forwardCardinality": "ONE_TO_MANY",
    "inverseId": 469,
    "inverseCardinality": "ONE_TO_MANY",
    "labeledTypes": []
  }, {
    "id": "contact_postal_mail",
    "fromObject": "contact",
    "toObject": "postal_mail",
    "forwardId": 454,
    "forwardCardinality": "ONE_TO_MANY",
    "inverseId": 453,
    "inverseCardinality": "ONE_TO_MANY",
    "labeledTypes": []
  }, {
    "id": "contact_project",
    "fromObject": "contact",
    "toObject": "project",
    "forwardId": 1243,
    "forwardCardinality": "ONE_TO_MANY",
    "inverseId": 1242,
    "inverseCardinality": "ONE_TO_MANY",
    "labeledTypes": []
  }, {
    "id": "contact_quote",
    "fromObject": "contact",
    "toObject": "quote",
    "forwardId": 70,
    "forwardCardinality": "ONE_TO_MANY",
    "inverseId": 69,
    "inverseCardinality": "ONE_TO_MANY",
    "labeledTypes": [{
      "label": "Billing",
      "forwardId": 1227,
      "forwardCardinality": "ONE_TO_MANY",
      "inverseId": 1226,
      "inverseCardinality": "ONE_TO_ONE",
      "isPrimary": false
    }, {
      "label": "Contact Signer",
      "forwardId": 703,
      "forwardCardinality": "ONE_TO_MANY",
      "inverseId": 702,
      "inverseCardinality": "ONE_TO_MANY",
      "isPrimary": false
    }]
  }, {
    "id": "contact_service",
    "fromObject": "contact",
    "toObject": "service",
    "forwardId": 799,
    "forwardCardinality": "ONE_TO_MANY",
    "inverseId": 798,
    "inverseCardinality": "ONE_TO_MANY",
    "labeledTypes": []
  }, {
    "id": "contact_subscription",
    "fromObject": "contact",
    "toObject": "subscription",
    "forwardId": 296,
    "forwardCardinality": "ONE_TO_MANY",
    "inverseId": 295,
    "inverseCardinality": "ONE_TO_ONE",
    "labeledTypes": []
  }, {
    "id": "contact_task",
    "fromObject": "contact",
    "toObject": "task",
    "forwardId": 203,
    "forwardCardinality": "ONE_TO_MANY",
    "inverseId": 204,
    "inverseCardinality": "ONE_TO_MANY",
    "labeledTypes": []
  }, {
    "id": "contact_ticket",
    "fromObject": "contact",
    "toObject": "ticket",
    "forwardId": 15,
    "forwardCardinality": "ONE_TO_MANY",
    "inverseId": 16,
    "inverseCardinality": "ONE_TO_MANY",
    "labeledTypes": []
  }, {
    "id": "conversation_call",
    "fromObject": "conversation",
    "toObject": "call",
    "forwardId": 629,
    "forwardCardinality": "ONE_TO_MANY",
    "inverseId": 628,
    "inverseCardinality": "ONE_TO_ONE",
    "labeledTypes": []
  }, {
    "id": "conversation_communication",
    "fromObject": "conversation",
    "toObject": "communication",
    "forwardId": 90,
    "forwardCardinality": "ONE_TO_MANY",
    "inverseId": 89,
    "inverseCardinality": "ONE_TO_ONE",
    "labeledTypes": []
  }, {
    "id": "conversation_feedback_submission",
    "fromObject": "conversation",
    "toObject": "feedback_submission",
    "forwardId": 331,
    "forwardCardinality": "ONE_TO_MANY",
    "inverseId": 332,
    "inverseCardinality": "ONE_TO_ONE",
    "labeledTypes": []
  }, {
    "id": "course_course",
    "fromObject": "course",
    "toObject": "course",
    "forwardId": 948,
    "forwardCardinality": "ONE_TO_MANY",
    "inverseId": 948,
    "inverseCardinality": "ONE_TO_MANY",
    "labeledTypes": []
  }, {
    "id": "deal_appointment",
    "fromObject": "deal",
    "toObject": "appointment",
    "forwardId": 945,
    "forwardCardinality": "ONE_TO_MANY",
    "inverseId": 944,
    "inverseCardinality": "ONE_TO_MANY",
    "labeledTypes": []
  }, {
    "id": "deal_call",
    "fromObject": "deal",
    "toObject": "call",
    "forwardId": 205,
    "forwardCardinality": "ONE_TO_MANY",
    "inverseId": 206,
    "inverseCardinality": "ONE_TO_MANY",
    "labeledTypes": []
  }, {
    "id": "deal_campaign",
    "fromObject": "deal",
    "toObject": "campaign",
    "forwardId": 1116,
    "forwardCardinality": "ONE_TO_MANY",
    "inverseId": 1115,
    "inverseCardinality": "ONE_TO_MANY",
    "labeledTypes": []
  }, {
    "id": "deal_cart",
    "fromObject": "deal",
    "toObject": "cart",
    "forwardId": 1346,
    "forwardCardinality": "ONE_TO_MANY",
    "inverseId": 1347,
    "inverseCardinality": "ONE_TO_MANY",
    "labeledTypes": []
  }, {
    "id": "deal_commerce_payment",
    "fromObject": "deal",
    "toObject": "commerce_payment",
    "forwardId": 392,
    "forwardCardinality": "ONE_TO_MANY",
    "inverseId": 391,
    "inverseCardinality": "ONE_TO_MANY",
    "labeledTypes": []
  }, {
    "id": "deal_communication",
    "fromObject": "deal",
    "toObject": "communication",
    "forwardId": 86,
    "forwardCardinality": "ONE_TO_MANY",
    "inverseId": 85,
    "inverseCardinality": "ONE_TO_MANY",
    "labeledTypes": []
  }, {
    "id": "deal_course",
    "fromObject": "deal",
    "toObject": "course",
    "forwardId": 863,
    "forwardCardinality": "ONE_TO_MANY",
    "inverseId": 862,
    "inverseCardinality": "ONE_TO_MANY",
    "labeledTypes": []
  }, {
    "id": "deal_deal",
    "fromObject": "deal",
    "toObject": "deal",
    "forwardId": 451,
    "forwardCardinality": "ONE_TO_MANY",
    "inverseId": 451,
    "inverseCardinality": "ONE_TO_MANY",
    "labeledTypes": []
  }, {
    "id": "deal_deal_split",
    "fromObject": "deal",
    "toObject": "deal_split",
    "forwardId": 313,
    "forwardCardinality": "ONE_TO_MANY",
    "inverseId": 314,
    "inverseCardinality": "ONE_TO_ONE",
    "labeledTypes": []
  }, {
    "id": "deal_discount",
    "fromObject": "deal",
    "toObject": "discount",
    "forwardId": 370,
    "forwardCardinality": "ONE_TO_MANY",
    "inverseId": 369,
    "inverseCardinality": "ONE_TO_ONE",
    "labeledTypes": []
  }, {
    "id": "deal_email",
    "fromObject": "deal",
    "toObject": "email",
    "forwardId": 209,
    "forwardCardinality": "ONE_TO_MANY",
    "inverseId": 210,
    "inverseCardinality": "ONE_TO_MANY",
    "labeledTypes": []
  }, {
    "id": "deal_feedback_submission",
    "fromObject": "deal",
    "toObject": "feedback_submission",
    "forwardId": 985,
    "forwardCardinality": "ONE_TO_MANY",
    "inverseId": 984,
    "inverseCardinality": "ONE_TO_ONE",
    "labeledTypes": []
  }, {
    "id": "deal_goal_target",
    "fromObject": "deal",
    "toObject": "goal_target",
    "forwardId": 1357,
    "forwardCardinality": "ONE_TO_MANY",
    "inverseId": 1356,
    "inverseCardinality": "ONE_TO_MANY",
    "labeledTypes": []
  }, {
    "id": "deal_invoice",
    "fromObject": "deal",
    "toObject": "invoice",
    "forwardId": 176,
    "forwardCardinality": "ONE_TO_MANY",
    "inverseId": 175,
    "inverseCardinality": "ONE_TO_MANY",
    "labeledTypes": []
  }, {
    "id": "deal_lead",
    "fromObject": "deal",
    "toObject": "lead",
    "forwardId": 1038,
    "forwardCardinality": "ONE_TO_MANY",
    "inverseId": 1037,
    "inverseCardinality": "ONE_TO_MANY",
    "labeledTypes": [{
      "label": "Primary",
      "forwardId": 583,
      "forwardCardinality": "ONE_TO_MANY",
      "inverseId": 582,
      "inverseCardinality": "ONE_TO_ONE",
      "isPrimary": true
    }]
  }, {
    "id": "deal_line_item",
    "fromObject": "deal",
    "toObject": "line_item",
    "forwardId": 19,
    "forwardCardinality": "ONE_TO_MANY",
    "inverseId": 20,
    "inverseCardinality": "ONE_TO_ONE",
    "labeledTypes": []
  }, {
    "id": "deal_listing",
    "fromObject": "deal",
    "toObject": "listing",
    "forwardId": 887,
    "forwardCardinality": "ONE_TO_MANY",
    "inverseId": 886,
    "inverseCardinality": "ONE_TO_MANY",
    "labeledTypes": []
  }, {
    "id": "deal_marketing_event",
    "fromObject": "deal",
    "toObject": "marketing_event",
    "forwardId": 1389,
    "forwardCardinality": "ONE_TO_MANY",
    "inverseId": 1388,
    "inverseCardinality": "ONE_TO_MANY",
    "labeledTypes": []
  }, {
    "id": "deal_meeting",
    "fromObject": "deal",
    "toObject": "meeting",
    "forwardId": 211,
    "forwardCardinality": "ONE_TO_MANY",
    "inverseId": 212,
    "inverseCardinality": "ONE_TO_MANY",
    "labeledTypes": []
  }, {
    "id": "deal_note",
    "fromObject": "deal",
    "toObject": "note",
    "forwardId": 213,
    "forwardCardinality": "ONE_TO_MANY",
    "inverseId": 214,
    "inverseCardinality": "ONE_TO_MANY",
    "labeledTypes": []
  }, {
    "id": "deal_order",
    "fromObject": "deal",
    "toObject": "order",
    "forwardId": 511,
    "forwardCardinality": "ONE_TO_MANY",
    "inverseId": 512,
    "inverseCardinality": "ONE_TO_MANY",
    "labeledTypes": []
  }, {
    "id": "deal_payment_link",
    "fromObject": "deal",
    "toObject": "payment_link",
    "forwardId": 474,
    "forwardCardinality": "ONE_TO_MANY",
    "inverseId": 473,
    "inverseCardinality": "ONE_TO_MANY",
    "labeledTypes": []
  }, {
    "id": "deal_postal_mail",
    "fromObject": "deal",
    "toObject": "postal_mail",
    "forwardId": 458,
    "forwardCardinality": "ONE_TO_MANY",
    "inverseId": 457,
    "inverseCardinality": "ONE_TO_MANY",
    "labeledTypes": []
  }, {
    "id": "deal_product",
    "fromObject": "deal",
    "toObject": "product",
    "forwardId": 630,
    "forwardCardinality": "ONE_TO_MANY",
    "inverseId": 631,
    "inverseCardinality": "ONE_TO_MANY",
    "labeledTypes": []
  }, {
    "id": "deal_project",
    "fromObject": "deal",
    "toObject": "project",
    "forwardId": 1239,
    "forwardCardinality": "ONE_TO_MANY",
    "inverseId": 1238,
    "inverseCardinality": "ONE_TO_MANY",
    "labeledTypes": []
  }, {
    "id": "deal_quote",
    "fromObject": "deal",
    "toObject": "quote",
    "forwardId": 63,
    "forwardCardinality": "ONE_TO_MANY",
    "inverseId": 64,
    "inverseCardinality": "ONE_TO_MANY",
    "labeledTypes": [{
      "label": "Primary",
      "forwardId": 1392,
      "forwardCardinality": "ONE_TO_ONE",
      "inverseId": 1393,
      "inverseCardinality": "ONE_TO_MANY",
      "isPrimary": true
    }]
  }, {
    "id": "deal_service",
    "fromObject": "deal",
    "toObject": "service",
    "forwardId": 795,
    "forwardCardinality": "ONE_TO_MANY",
    "inverseId": 794,
    "inverseCardinality": "ONE_TO_MANY",
    "labeledTypes": []
  }, {
    "id": "deal_subscription",
    "fromObject": "deal",
    "toObject": "subscription",
    "forwardId": 300,
    "forwardCardinality": "ONE_TO_MANY",
    "inverseId": 299,
    "inverseCardinality": "ONE_TO_MANY",
    "labeledTypes": []
  }, {
    "id": "deal_task",
    "fromObject": "deal",
    "toObject": "task",
    "forwardId": 215,
    "forwardCardinality": "ONE_TO_MANY",
    "inverseId": 216,
    "inverseCardinality": "ONE_TO_MANY",
    "labeledTypes": []
  }, {
    "id": "deal_ticket",
    "fromObject": "deal",
    "toObject": "ticket",
    "forwardId": 27,
    "forwardCardinality": "ONE_TO_MANY",
    "inverseId": 28,
    "inverseCardinality": "ONE_TO_MANY",
    "labeledTypes": []
  }, {
    "id": "discount_cart",
    "fromObject": "discount",
    "toObject": "cart",
    "forwardId": 589,
    "forwardCardinality": "ONE_TO_ONE",
    "inverseId": 588,
    "inverseCardinality": "ONE_TO_MANY",
    "labeledTypes": []
  }, {
    "id": "discount_code_order",
    "fromObject": "discount_code",
    "toObject": "order",
    "forwardId": 522,
    "forwardCardinality": "ONE_TO_MANY",
    "inverseId": 521,
    "inverseCardinality": "ONE_TO_MANY",
    "labeledTypes": []
  }, {
    "id": "discount_commerce_payment",
    "fromObject": "discount",
    "toObject": "commerce_payment",
    "forwardId": 427,
    "forwardCardinality": "ONE_TO_ONE",
    "inverseId": 428,
    "inverseCardinality": "ONE_TO_MANY",
    "labeledTypes": []
  }, {
    "id": "discount_discount_code",
    "fromObject": "discount",
    "toObject": "discount_code",
    "forwardId": 424,
    "forwardCardinality": "ONE_TO_ONE",
    "inverseId": 423,
    "inverseCardinality": "ONE_TO_MANY",
    "labeledTypes": []
  }, {
    "id": "discount_order",
    "fromObject": "discount",
    "toObject": "order",
    "forwardId": 520,
    "forwardCardinality": "ONE_TO_ONE",
    "inverseId": 519,
    "inverseCardinality": "ONE_TO_MANY",
    "labeledTypes": []
  }, {
    "id": "discount_payment_link",
    "fromObject": "discount",
    "toObject": "payment_link",
    "forwardId": 683,
    "forwardCardinality": "ONE_TO_ONE",
    "inverseId": 682,
    "inverseCardinality": "ONE_TO_MANY",
    "labeledTypes": []
  }, {
    "id": "email_appointment",
    "fromObject": "email",
    "toObject": "appointment",
    "forwardId": 917,
    "forwardCardinality": "ONE_TO_MANY",
    "inverseId": 916,
    "inverseCardinality": "ONE_TO_MANY",
    "labeledTypes": []
  }, {
    "id": "email_cart",
    "fromObject": "email",
    "toObject": "cart",
    "forwardId": 779,
    "forwardCardinality": "ONE_TO_MANY",
    "inverseId": 778,
    "inverseCardinality": "ONE_TO_MANY",
    "labeledTypes": []
  }, {
    "id": "email_course",
    "fromObject": "email",
    "toObject": "course",
    "forwardId": 871,
    "forwardCardinality": "ONE_TO_MANY",
    "inverseId": 870,
    "inverseCardinality": "ONE_TO_MANY",
    "labeledTypes": []
  }, {
    "id": "email_lead",
    "fromObject": "email",
    "toObject": "lead",
    "forwardId": 599,
    "forwardCardinality": "ONE_TO_MANY",
    "inverseId": 598,
    "inverseCardinality": "ONE_TO_MANY",
    "labeledTypes": []
  }, {
    "id": "email_listing",
    "fromObject": "email",
    "toObject": "listing",
    "forwardId": 895,
    "forwardCardinality": "ONE_TO_MANY",
    "inverseId": 894,
    "inverseCardinality": "ONE_TO_MANY",
    "labeledTypes": []
  }, {
    "id": "email_marketing_event",
    "fromObject": "email",
    "toObject": "marketing_event",
    "forwardId": 498,
    "forwardCardinality": "ONE_TO_MANY",
    "inverseId": 497,
    "inverseCardinality": "ONE_TO_MANY",
    "labeledTypes": []
  }, {
    "id": "email_order",
    "fromObject": "email",
    "toObject": "order",
    "forwardId": 777,
    "forwardCardinality": "ONE_TO_MANY",
    "inverseId": 776,
    "inverseCardinality": "ONE_TO_MANY",
    "labeledTypes": []
  }, {
    "id": "email_project",
    "fromObject": "email",
    "toObject": "project",
    "forwardId": 1260,
    "forwardCardinality": "ONE_TO_MANY",
    "inverseId": 1259,
    "inverseCardinality": "ONE_TO_MANY",
    "labeledTypes": []
  }, {
    "id": "email_service",
    "fromObject": "email",
    "toObject": "service",
    "forwardId": 843,
    "forwardCardinality": "ONE_TO_MANY",
    "inverseId": 842,
    "inverseCardinality": "ONE_TO_MANY",
    "labeledTypes": []
  }, {
    "id": "fee_commerce_payment",
    "fromObject": "fee",
    "toObject": "commerce_payment",
    "forwardId": 1327,
    "forwardCardinality": "ONE_TO_ONE",
    "inverseId": 1326,
    "inverseCardinality": "ONE_TO_MANY",
    "labeledTypes": []
  }, {
    "id": "fee_payment_link",
    "fromObject": "fee",
    "toObject": "payment_link",
    "forwardId": 687,
    "forwardCardinality": "ONE_TO_ONE",
    "inverseId": 686,
    "inverseCardinality": "ONE_TO_MANY",
    "labeledTypes": []
  }, {
    "id": "feedback_submission_call",
    "fromObject": "feedback_submission",
    "toObject": "call",
    "forwardId": 1155,
    "forwardCardinality": "ONE_TO_ONE",
    "inverseId": 1156,
    "inverseCardinality": "ONE_TO_MANY",
    "labeledTypes": []
  }, {
    "id": "feedback_submission_cart",
    "fromObject": "feedback_submission",
    "toObject": "cart",
    "forwardId": 1157,
    "forwardCardinality": "ONE_TO_ONE",
    "inverseId": 1158,
    "inverseCardinality": "ONE_TO_MANY",
    "labeledTypes": []
  }, {
    "id": "feedback_submission_commerce_payment",
    "fromObject": "feedback_submission",
    "toObject": "commerce_payment",
    "forwardId": 1169,
    "forwardCardinality": "ONE_TO_ONE",
    "inverseId": 1170,
    "inverseCardinality": "ONE_TO_MANY",
    "labeledTypes": []
  }, {
    "id": "feedback_submission_invoice",
    "fromObject": "feedback_submission",
    "toObject": "invoice",
    "forwardId": 1159,
    "forwardCardinality": "ONE_TO_ONE",
    "inverseId": 1160,
    "inverseCardinality": "ONE_TO_MANY",
    "labeledTypes": []
  }, {
    "id": "feedback_submission_lead",
    "fromObject": "feedback_submission",
    "toObject": "lead",
    "forwardId": 1161,
    "forwardCardinality": "ONE_TO_ONE",
    "inverseId": 1162,
    "inverseCardinality": "ONE_TO_MANY",
    "labeledTypes": []
  }, {
    "id": "feedback_submission_marketing_event",
    "fromObject": "feedback_submission",
    "toObject": "marketing_event",
    "forwardId": 1163,
    "forwardCardinality": "ONE_TO_ONE",
    "inverseId": 1164,
    "inverseCardinality": "ONE_TO_MANY",
    "labeledTypes": []
  }, {
    "id": "feedback_submission_order",
    "fromObject": "feedback_submission",
    "toObject": "order",
    "forwardId": 1167,
    "forwardCardinality": "ONE_TO_ONE",
    "inverseId": 1168,
    "inverseCardinality": "ONE_TO_MANY",
    "labeledTypes": []
  }, {
    "id": "feedback_submission_subscription",
    "fromObject": "feedback_submission",
    "toObject": "subscription",
    "forwardId": 1173,
    "forwardCardinality": "ONE_TO_ONE",
    "inverseId": 1174,
    "inverseCardinality": "ONE_TO_MANY",
    "labeledTypes": []
  }, {
    "id": "invoice_commerce_payment",
    "fromObject": "invoice",
    "toObject": "commerce_payment",
    "forwardId": 541,
    "forwardCardinality": "ONE_TO_MANY",
    "inverseId": 542,
    "inverseCardinality": "ONE_TO_ONE",
    "labeledTypes": []
  }, {
    "id": "invoice_discount",
    "fromObject": "invoice",
    "toObject": "discount",
    "forwardId": 411,
    "forwardCardinality": "ONE_TO_MANY",
    "inverseId": 412,
    "inverseCardinality": "ONE_TO_ONE",
    "labeledTypes": []
  }, {
    "id": "invoice_fee",
    "fromObject": "invoice",
    "toObject": "fee",
    "forwardId": 413,
    "forwardCardinality": "ONE_TO_MANY",
    "inverseId": 414,
    "inverseCardinality": "ONE_TO_ONE",
    "labeledTypes": []
  }, {
    "id": "invoice_order",
    "fromObject": "invoice",
    "toObject": "order",
    "forwardId": 517,
    "forwardCardinality": "ONE_TO_ONE",
    "inverseId": 518,
    "inverseCardinality": "ONE_TO_MANY",
    "labeledTypes": []
  }, {
    "id": "invoice_payment_link",
    "fromObject": "invoice",
    "toObject": "payment_link",
    "forwardId": 815,
    "forwardCardinality": "ONE_TO_ONE",
    "inverseId": 814,
    "inverseCardinality": "ONE_TO_MANY",
    "labeledTypes": []
  }, {
    "id": "invoice_subscription",
    "fromObject": "invoice",
    "toObject": "subscription",
    "forwardId": 622,
    "forwardCardinality": "ONE_TO_MANY",
    "inverseId": 623,
    "inverseCardinality": "ONE_TO_MANY",
    "labeledTypes": []
  }, {
    "id": "invoice_tax",
    "fromObject": "invoice",
    "toObject": "tax",
    "forwardId": 415,
    "forwardCardinality": "ONE_TO_MANY",
    "inverseId": 416,
    "inverseCardinality": "ONE_TO_ONE",
    "labeledTypes": []
  }, {
    "id": "line_item_cart",
    "fromObject": "line_item",
    "toObject": "cart",
    "forwardId": 591,
    "forwardCardinality": "ONE_TO_ONE",
    "inverseId": 590,
    "inverseCardinality": "ONE_TO_MANY",
    "labeledTypes": []
  }, {
    "id": "line_item_commerce_payment",
    "fromObject": "line_item",
    "toObject": "commerce_payment",
    "forwardId": 396,
    "forwardCardinality": "ONE_TO_MANY",
    "inverseId": 395,
    "inverseCardinality": "ONE_TO_MANY",
    "labeledTypes": []
  }, {
    "id": "line_item_discount",
    "fromObject": "line_item",
    "toObject": "discount",
    "forwardId": 368,
    "forwardCardinality": "ONE_TO_MANY",
    "inverseId": 367,
    "inverseCardinality": "ONE_TO_ONE",
    "labeledTypes": []
  }, {
    "id": "line_item_invoice",
    "fromObject": "line_item",
    "toObject": "invoice",
    "forwardId": 410,
    "forwardCardinality": "ONE_TO_ONE",
    "inverseId": 409,
    "inverseCardinality": "ONE_TO_MANY",
    "labeledTypes": []
  }, {
    "id": "line_item_line_item",
    "fromObject": "line_item",
    "toObject": "line_item",
    "forwardId": 2532,
    "forwardCardinality": "ONE_TO_MANY",
    "inverseId": 2532,
    "inverseCardinality": "ONE_TO_MANY",
    "labeledTypes": [{
      "label": "Parent",
      "inverseLabel": "Child",
      "forwardId": 2541,
      "forwardCardinality": "ONE_TO_ONE",
      "inverseId": 2542,
      "inverseCardinality": "ONE_TO_MANY",
      "isPrimary": false
    }]
  }, {
    "id": "line_item_order",
    "fromObject": "line_item",
    "toObject": "order",
    "forwardId": 514,
    "forwardCardinality": "ONE_TO_ONE",
    "inverseId": 513,
    "inverseCardinality": "ONE_TO_MANY",
    "labeledTypes": []
  }, {
    "id": "line_item_payment_link",
    "fromObject": "line_item",
    "toObject": "payment_link",
    "forwardId": 759,
    "forwardCardinality": "ONE_TO_ONE",
    "inverseId": 758,
    "inverseCardinality": "ONE_TO_MANY",
    "labeledTypes": []
  }, {
    "id": "line_item_quote",
    "fromObject": "line_item",
    "toObject": "quote",
    "forwardId": 68,
    "forwardCardinality": "ONE_TO_ONE",
    "inverseId": 67,
    "inverseCardinality": "ONE_TO_MANY",
    "labeledTypes": []
  }, {
    "id": "line_item_quote_template",
    "fromObject": "line_item",
    "toObject": "quote_template",
    "forwardId": 1268,
    "forwardCardinality": "ONE_TO_ONE",
    "inverseId": 1267,
    "inverseCardinality": "ONE_TO_MANY",
    "labeledTypes": []
  }, {
    "id": "line_item_subscription",
    "fromObject": "line_item",
    "toObject": "subscription",
    "forwardId": 302,
    "forwardCardinality": "ONE_TO_ONE",
    "inverseId": 301,
    "inverseCardinality": "ONE_TO_MANY",
    "labeledTypes": [{
      "label": "Upcoming",
      "forwardId": 565,
      "forwardCardinality": "ONE_TO_ONE",
      "inverseId": 564,
      "inverseCardinality": "ONE_TO_MANY",
      "isPrimary": false
    }]
  }, {
    "id": "listing_listing",
    "fromObject": "listing",
    "toObject": "listing",
    "forwardId": 949,
    "forwardCardinality": "ONE_TO_MANY",
    "inverseId": 949,
    "inverseCardinality": "ONE_TO_MANY",
    "labeledTypes": []
  }, {
    "id": "marketing_event_lead",
    "fromObject": "marketing_event",
    "toObject": "lead",
    "forwardId": 1390,
    "forwardCardinality": "ONE_TO_MANY",
    "inverseId": 1391,
    "inverseCardinality": "ONE_TO_MANY",
    "labeledTypes": []
  }, {
    "id": "marketing_event_project",
    "fromObject": "marketing_event",
    "toObject": "project",
    "forwardId": 1251,
    "forwardCardinality": "ONE_TO_MANY",
    "inverseId": 1250,
    "inverseCardinality": "ONE_TO_MANY",
    "labeledTypes": []
  }, {
    "id": "meeting_appointment",
    "fromObject": "meeting",
    "toObject": "appointment",
    "forwardId": 919,
    "forwardCardinality": "ONE_TO_MANY",
    "inverseId": 918,
    "inverseCardinality": "ONE_TO_MANY",
    "labeledTypes": []
  }, {
    "id": "meeting_call",
    "fromObject": "meeting",
    "toObject": "call",
    "forwardId": 400,
    "forwardCardinality": "ONE_TO_MANY",
    "inverseId": 399,
    "inverseCardinality": "ONE_TO_MANY",
    "labeledTypes": []
  }, {
    "id": "meeting_cart",
    "fromObject": "meeting",
    "toObject": "cart",
    "forwardId": 771,
    "forwardCardinality": "ONE_TO_MANY",
    "inverseId": 770,
    "inverseCardinality": "ONE_TO_MANY",
    "labeledTypes": []
  }, {
    "id": "meeting_course",
    "fromObject": "meeting",
    "toObject": "course",
    "forwardId": 873,
    "forwardCardinality": "ONE_TO_MANY",
    "inverseId": 872,
    "inverseCardinality": "ONE_TO_MANY",
    "labeledTypes": []
  }, {
    "id": "meeting_email",
    "fromObject": "meeting",
    "toObject": "email",
    "forwardId": 1128,
    "forwardCardinality": "ONE_TO_MANY",
    "inverseId": 1127,
    "inverseCardinality": "ONE_TO_MANY",
    "labeledTypes": []
  }, {
    "id": "meeting_lead",
    "fromObject": "meeting",
    "toObject": "lead",
    "forwardId": 601,
    "forwardCardinality": "ONE_TO_MANY",
    "inverseId": 600,
    "inverseCardinality": "ONE_TO_MANY",
    "labeledTypes": []
  }, {
    "id": "meeting_listing",
    "fromObject": "meeting",
    "toObject": "listing",
    "forwardId": 897,
    "forwardCardinality": "ONE_TO_MANY",
    "inverseId": 896,
    "inverseCardinality": "ONE_TO_MANY",
    "labeledTypes": []
  }, {
    "id": "meeting_marketing_event",
    "fromObject": "meeting",
    "toObject": "marketing_event",
    "forwardId": 500,
    "forwardCardinality": "ONE_TO_MANY",
    "inverseId": 499,
    "inverseCardinality": "ONE_TO_MANY",
    "labeledTypes": []
  }, {
    "id": "meeting_order",
    "fromObject": "meeting",
    "toObject": "order",
    "forwardId": 769,
    "forwardCardinality": "ONE_TO_MANY",
    "inverseId": 768,
    "inverseCardinality": "ONE_TO_MANY",
    "labeledTypes": []
  }, {
    "id": "meeting_project",
    "fromObject": "meeting",
    "toObject": "project",
    "forwardId": 1256,
    "forwardCardinality": "ONE_TO_MANY",
    "inverseId": 1255,
    "inverseCardinality": "ONE_TO_MANY",
    "labeledTypes": []
  }, {
    "id": "meeting_service",
    "fromObject": "meeting",
    "toObject": "service",
    "forwardId": 839,
    "forwardCardinality": "ONE_TO_MANY",
    "inverseId": 838,
    "inverseCardinality": "ONE_TO_MANY",
    "labeledTypes": []
  }, {
    "id": "note_appointment",
    "fromObject": "note",
    "toObject": "appointment",
    "forwardId": 921,
    "forwardCardinality": "ONE_TO_MANY",
    "inverseId": 920,
    "inverseCardinality": "ONE_TO_MANY",
    "labeledTypes": []
  }, {
    "id": "note_cart",
    "fromObject": "note",
    "toObject": "cart",
    "forwardId": 767,
    "forwardCardinality": "ONE_TO_MANY",
    "inverseId": 766,
    "inverseCardinality": "ONE_TO_MANY",
    "labeledTypes": []
  }, {
    "id": "note_course",
    "fromObject": "note",
    "toObject": "course",
    "forwardId": 875,
    "forwardCardinality": "ONE_TO_MANY",
    "inverseId": 874,
    "inverseCardinality": "ONE_TO_MANY",
    "labeledTypes": []
  }, {
    "id": "note_lead",
    "fromObject": "note",
    "toObject": "lead",
    "forwardId": 855,
    "forwardCardinality": "ONE_TO_MANY",
    "inverseId": 854,
    "inverseCardinality": "ONE_TO_MANY",
    "labeledTypes": []
  }, {
    "id": "note_listing",
    "fromObject": "note",
    "toObject": "listing",
    "forwardId": 899,
    "forwardCardinality": "ONE_TO_MANY",
    "inverseId": 898,
    "inverseCardinality": "ONE_TO_MANY",
    "labeledTypes": []
  }, {
    "id": "note_marketing_event",
    "fromObject": "note",
    "toObject": "marketing_event",
    "forwardId": 260,
    "forwardCardinality": "ONE_TO_MANY",
    "inverseId": 259,
    "inverseCardinality": "ONE_TO_MANY",
    "labeledTypes": []
  }, {
    "id": "note_order",
    "fromObject": "note",
    "toObject": "order",
    "forwardId": 765,
    "forwardCardinality": "ONE_TO_MANY",
    "inverseId": 764,
    "inverseCardinality": "ONE_TO_MANY",
    "labeledTypes": []
  }, {
    "id": "note_project",
    "fromObject": "note",
    "toObject": "project",
    "forwardId": 1249,
    "forwardCardinality": "ONE_TO_MANY",
    "inverseId": 1248,
    "inverseCardinality": "ONE_TO_MANY",
    "labeledTypes": []
  }, {
    "id": "note_service",
    "fromObject": "note",
    "toObject": "service",
    "forwardId": 837,
    "forwardCardinality": "ONE_TO_MANY",
    "inverseId": 836,
    "inverseCardinality": "ONE_TO_MANY",
    "labeledTypes": []
  }, {
    "id": "order_cart",
    "fromObject": "order",
    "toObject": "cart",
    "forwardId": 593,
    "forwardCardinality": "ONE_TO_MANY",
    "inverseId": 592,
    "inverseCardinality": "ONE_TO_ONE",
    "labeledTypes": []
  }, {
    "id": "postal_mail_appointment",
    "fromObject": "postal_mail",
    "toObject": "appointment",
    "forwardId": 927,
    "forwardCardinality": "ONE_TO_MANY",
    "inverseId": 926,
    "inverseCardinality": "ONE_TO_MANY",
    "labeledTypes": []
  }, {
    "id": "postal_mail_cart",
    "fromObject": "postal_mail",
    "toObject": "cart",
    "forwardId": 790,
    "forwardCardinality": "ONE_TO_MANY",
    "inverseId": 791,
    "inverseCardinality": "ONE_TO_MANY",
    "labeledTypes": []
  }, {
    "id": "postal_mail_course",
    "fromObject": "postal_mail",
    "toObject": "course",
    "forwardId": 881,
    "forwardCardinality": "ONE_TO_MANY",
    "inverseId": 880,
    "inverseCardinality": "ONE_TO_MANY",
    "labeledTypes": []
  }, {
    "id": "postal_mail_listing",
    "fromObject": "postal_mail",
    "toObject": "listing",
    "forwardId": 905,
    "forwardCardinality": "ONE_TO_MANY",
    "inverseId": 904,
    "inverseCardinality": "ONE_TO_MANY",
    "labeledTypes": []
  }, {
    "id": "postal_mail_order",
    "fromObject": "postal_mail",
    "toObject": "order",
    "forwardId": 788,
    "forwardCardinality": "ONE_TO_MANY",
    "inverseId": 789,
    "inverseCardinality": "ONE_TO_MANY",
    "labeledTypes": []
  }, {
    "id": "postal_mail_project",
    "fromObject": "postal_mail",
    "toObject": "project",
    "forwardId": 1264,
    "forwardCardinality": "ONE_TO_MANY",
    "inverseId": 1263,
    "inverseCardinality": "ONE_TO_MANY",
    "labeledTypes": []
  }, {
    "id": "postal_mail_service",
    "fromObject": "postal_mail",
    "toObject": "service",
    "forwardId": 849,
    "forwardCardinality": "ONE_TO_MANY",
    "inverseId": 848,
    "inverseCardinality": "ONE_TO_MANY",
    "labeledTypes": []
  }, {
    "id": "product_product",
    "fromObject": "product",
    "toObject": "product",
    "forwardId": 1211,
    "forwardCardinality": "ONE_TO_MANY",
    "inverseId": 1211,
    "inverseCardinality": "ONE_TO_MANY",
    "labeledTypes": [{
      "label": "Product",
      "inverseLabel": "Product Bundle",
      "forwardId": 1209,
      "forwardCardinality": "ONE_TO_MANY",
      "inverseId": 1210,
      "inverseCardinality": "ONE_TO_MANY",
      "isPrimary": false
    }, {
      "label": "Product",
      "inverseLabel": "Product Variant",
      "forwardId": 1342,
      "forwardCardinality": "ONE_TO_ONE",
      "inverseId": 1343,
      "inverseCardinality": "ONE_TO_MANY",
      "isPrimary": false
    }]
  }, {
    "id": "project_project",
    "fromObject": "project",
    "toObject": "project",
    "forwardId": 1254,
    "forwardCardinality": "ONE_TO_MANY",
    "inverseId": 1254,
    "inverseCardinality": "ONE_TO_MANY",
    "labeledTypes": []
  }, {
    "id": "quote_cart",
    "fromObject": "quote",
    "toObject": "cart",
    "forwardId": 733,
    "forwardCardinality": "ONE_TO_ONE",
    "inverseId": 732,
    "inverseCardinality": "ONE_TO_MANY",
    "labeledTypes": []
  }, {
    "id": "quote_commerce_payment",
    "fromObject": "quote",
    "toObject": "commerce_payment",
    "forwardId": 398,
    "forwardCardinality": "ONE_TO_MANY",
    "inverseId": 397,
    "inverseCardinality": "ONE_TO_ONE",
    "labeledTypes": []
  }, {
    "id": "quote_discount",
    "fromObject": "quote",
    "toObject": "discount",
    "forwardId": 362,
    "forwardCardinality": "ONE_TO_MANY",
    "inverseId": 361,
    "inverseCardinality": "ONE_TO_ONE",
    "labeledTypes": []
  }, {
    "id": "quote_fee",
    "fromObject": "quote",
    "toObject": "fee",
    "forwardId": 364,
    "forwardCardinality": "ONE_TO_MANY",
    "inverseId": 363,
    "inverseCardinality": "ONE_TO_ONE",
    "labeledTypes": []
  }, {
    "id": "quote_feedback_submission",
    "fromObject": "quote",
    "toObject": "feedback_submission",
    "forwardId": 1172,
    "forwardCardinality": "ONE_TO_MANY",
    "inverseId": 1171,
    "inverseCardinality": "ONE_TO_ONE",
    "labeledTypes": []
  }, {
    "id": "quote_invoice",
    "fromObject": "quote",
    "toObject": "invoice",
    "forwardId": 408,
    "forwardCardinality": "ONE_TO_MANY",
    "inverseId": 407,
    "inverseCardinality": "ONE_TO_ONE",
    "labeledTypes": []
  }, {
    "id": "quote_order",
    "fromObject": "quote",
    "toObject": "order",
    "forwardId": 731,
    "forwardCardinality": "ONE_TO_ONE",
    "inverseId": 730,
    "inverseCardinality": "ONE_TO_MANY",
    "labeledTypes": []
  }, {
    "id": "quote_quote_template",
    "fromObject": "quote",
    "toObject": "quote_template",
    "forwardId": 286,
    "forwardCardinality": "ONE_TO_ONE",
    "inverseId": 285,
    "inverseCardinality": "ONE_TO_MANY",
    "labeledTypes": []
  }, {
    "id": "quote_subscription",
    "fromObject": "quote",
    "toObject": "subscription",
    "forwardId": 304,
    "forwardCardinality": "ONE_TO_MANY",
    "inverseId": 303,
    "inverseCardinality": "ONE_TO_ONE",
    "labeledTypes": []
  }, {
    "id": "quote_task",
    "fromObject": "quote",
    "toObject": "task",
    "forwardId": 217,
    "forwardCardinality": "ONE_TO_MANY",
    "inverseId": 218,
    "inverseCardinality": "ONE_TO_MANY",
    "labeledTypes": []
  }, {
    "id": "quote_tax",
    "fromObject": "quote",
    "toObject": "tax",
    "forwardId": 366,
    "forwardCardinality": "ONE_TO_MANY",
    "inverseId": 365,
    "inverseCardinality": "ONE_TO_ONE",
    "labeledTypes": []
  }, {
    "id": "quote_template_user",
    "fromObject": "quote_template",
    "toObject": "user",
    "forwardId": 1271,
    "forwardCardinality": "ONE_TO_MANY",
    "inverseId": 1272,
    "inverseCardinality": "ONE_TO_MANY",
    "labeledTypes": []
  }, {
    "id": "quote_user",
    "fromObject": "quote",
    "toObject": "user",
    "forwardId": 704,
    "forwardCardinality": "ONE_TO_MANY",
    "inverseId": 705,
    "inverseCardinality": "ONE_TO_MANY",
    "labeledTypes": []
  }, {
    "id": "service_project",
    "fromObject": "service",
    "toObject": "project",
    "forwardId": 1245,
    "forwardCardinality": "ONE_TO_MANY",
    "inverseId": 1244,
    "inverseCardinality": "ONE_TO_MANY",
    "labeledTypes": []
  }, {
    "id": "service_service",
    "fromObject": "service",
    "toObject": "service",
    "forwardId": 951,
    "forwardCardinality": "ONE_TO_MANY",
    "inverseId": 951,
    "inverseCardinality": "ONE_TO_MANY",
    "labeledTypes": []
  }, {
    "id": "subscription_commerce_payment",
    "fromObject": "subscription",
    "toObject": "commerce_payment",
    "forwardId": 394,
    "forwardCardinality": "ONE_TO_MANY",
    "inverseId": 393,
    "inverseCardinality": "ONE_TO_MANY",
    "labeledTypes": []
  }, {
    "id": "subscription_order",
    "fromObject": "subscription",
    "toObject": "order",
    "forwardId": 515,
    "forwardCardinality": "ONE_TO_ONE",
    "inverseId": 516,
    "inverseCardinality": "ONE_TO_MANY",
    "labeledTypes": []
  }, {
    "id": "subscription_payment_link",
    "fromObject": "subscription",
    "toObject": "payment_link",
    "forwardId": 478,
    "forwardCardinality": "ONE_TO_ONE",
    "inverseId": 477,
    "inverseCardinality": "ONE_TO_MANY",
    "labeledTypes": []
  }, {
    "id": "task_appointment",
    "fromObject": "task",
    "toObject": "appointment",
    "forwardId": 923,
    "forwardCardinality": "ONE_TO_MANY",
    "inverseId": 922,
    "inverseCardinality": "ONE_TO_MANY",
    "labeledTypes": []
  }, {
    "id": "task_campaign",
    "fromObject": "task",
    "toObject": "campaign",
    "forwardId": 1266,
    "forwardCardinality": "ONE_TO_MANY",
    "inverseId": 1265,
    "inverseCardinality": "ONE_TO_MANY",
    "labeledTypes": []
  }, {
    "id": "task_cart",
    "fromObject": "task",
    "toObject": "cart",
    "forwardId": 729,
    "forwardCardinality": "ONE_TO_MANY",
    "inverseId": 728,
    "inverseCardinality": "ONE_TO_MANY",
    "labeledTypes": []
  }, {
    "id": "task_course",
    "fromObject": "task",
    "toObject": "course",
    "forwardId": 877,
    "forwardCardinality": "ONE_TO_MANY",
    "inverseId": 876,
    "inverseCardinality": "ONE_TO_MANY",
    "labeledTypes": []
  }, {
    "id": "task_lead",
    "fromObject": "task",
    "toObject": "lead",
    "forwardId": 647,
    "forwardCardinality": "ONE_TO_MANY",
    "inverseId": 646,
    "inverseCardinality": "ONE_TO_MANY",
    "labeledTypes": []
  }, {
    "id": "task_listing",
    "fromObject": "task",
    "toObject": "listing",
    "forwardId": 901,
    "forwardCardinality": "ONE_TO_MANY",
    "inverseId": 900,
    "inverseCardinality": "ONE_TO_MANY",
    "labeledTypes": []
  }, {
    "id": "task_marketing_event",
    "fromObject": "task",
    "toObject": "marketing_event",
    "forwardId": 262,
    "forwardCardinality": "ONE_TO_MANY",
    "inverseId": 261,
    "inverseCardinality": "ONE_TO_MANY",
    "labeledTypes": []
  }, {
    "id": "task_meeting",
    "fromObject": "task",
    "toObject": "meeting",
    "forwardId": 1093,
    "forwardCardinality": "ONE_TO_MANY",
    "inverseId": 1094,
    "inverseCardinality": "ONE_TO_MANY",
    "labeledTypes": []
  }, {
    "id": "task_order",
    "fromObject": "task",
    "toObject": "order",
    "forwardId": 727,
    "forwardCardinality": "ONE_TO_MANY",
    "inverseId": 726,
    "inverseCardinality": "ONE_TO_MANY",
    "labeledTypes": []
  }, {
    "id": "task_project",
    "fromObject": "task",
    "toObject": "project",
    "forwardId": 1247,
    "forwardCardinality": "ONE_TO_MANY",
    "inverseId": 1246,
    "inverseCardinality": "ONE_TO_MANY",
    "labeledTypes": []
  }, {
    "id": "task_service",
    "fromObject": "task",
    "toObject": "service",
    "forwardId": 853,
    "forwardCardinality": "ONE_TO_MANY",
    "inverseId": 852,
    "inverseCardinality": "ONE_TO_MANY",
    "labeledTypes": []
  }, {
    "id": "tax_payment_link",
    "fromObject": "tax",
    "toObject": "payment_link",
    "forwardId": 685,
    "forwardCardinality": "ONE_TO_ONE",
    "inverseId": 684,
    "inverseCardinality": "ONE_TO_MANY",
    "labeledTypes": []
  }, {
    "id": "ticket_appointment",
    "fromObject": "ticket",
    "toObject": "appointment",
    "forwardId": 947,
    "forwardCardinality": "ONE_TO_MANY",
    "inverseId": 946,
    "inverseCardinality": "ONE_TO_MANY",
    "labeledTypes": []
  }, {
    "id": "ticket_call",
    "fromObject": "ticket",
    "toObject": "call",
    "forwardId": 219,
    "forwardCardinality": "ONE_TO_MANY",
    "inverseId": 220,
    "inverseCardinality": "ONE_TO_MANY",
    "labeledTypes": []
  }, {
    "id": "ticket_campaign",
    "fromObject": "ticket",
    "toObject": "campaign",
    "forwardId": 1084,
    "forwardCardinality": "ONE_TO_MANY",
    "inverseId": 1083,
    "inverseCardinality": "ONE_TO_MANY",
    "labeledTypes": []
  }, {
    "id": "ticket_cart",
    "fromObject": "ticket",
    "toObject": "cart",
    "forwardId": 595,
    "forwardCardinality": "ONE_TO_MANY",
    "inverseId": 594,
    "inverseCardinality": "ONE_TO_MANY",
    "labeledTypes": []
  }, {
    "id": "ticket_commerce_payment",
    "fromObject": "ticket",
    "toObject": "commerce_payment",
    "forwardId": 1355,
    "forwardCardinality": "ONE_TO_MANY",
    "inverseId": 1354,
    "inverseCardinality": "ONE_TO_MANY",
    "labeledTypes": []
  }, {
    "id": "ticket_communication",
    "fromObject": "ticket",
    "toObject": "communication",
    "forwardId": 84,
    "forwardCardinality": "ONE_TO_MANY",
    "inverseId": 83,
    "inverseCardinality": "ONE_TO_MANY",
    "labeledTypes": []
  }, {
    "id": "ticket_conversation",
    "fromObject": "ticket",
    "toObject": "conversation",
    "forwardId": 278,
    "forwardCardinality": "ONE_TO_MANY",
    "inverseId": 277,
    "inverseCardinality": "ONE_TO_ONE",
    "labeledTypes": []
  }, {
    "id": "ticket_course",
    "fromObject": "ticket",
    "toObject": "course",
    "forwardId": 941,
    "forwardCardinality": "ONE_TO_MANY",
    "inverseId": 940,
    "inverseCardinality": "ONE_TO_MANY",
    "labeledTypes": []
  }, {
    "id": "ticket_email",
    "fromObject": "ticket",
    "toObject": "email",
    "forwardId": 223,
    "forwardCardinality": "ONE_TO_MANY",
    "inverseId": 224,
    "inverseCardinality": "ONE_TO_MANY",
    "labeledTypes": []
  }, {
    "id": "ticket_feedback_submission",
    "fromObject": "ticket",
    "toObject": "feedback_submission",
    "forwardId": 99,
    "forwardCardinality": "ONE_TO_MANY",
    "inverseId": 100,
    "inverseCardinality": "ONE_TO_ONE",
    "labeledTypes": []
  }, {
    "id": "ticket_invoice",
    "fromObject": "ticket",
    "toObject": "invoice",
    "forwardId": 987,
    "forwardCardinality": "ONE_TO_MANY",
    "inverseId": 986,
    "inverseCardinality": "ONE_TO_MANY",
    "labeledTypes": []
  }, {
    "id": "ticket_listing",
    "fromObject": "ticket",
    "toObject": "listing",
    "forwardId": 943,
    "forwardCardinality": "ONE_TO_MANY",
    "inverseId": 942,
    "inverseCardinality": "ONE_TO_MANY",
    "labeledTypes": []
  }, {
    "id": "ticket_meeting",
    "fromObject": "ticket",
    "toObject": "meeting",
    "forwardId": 225,
    "forwardCardinality": "ONE_TO_MANY",
    "inverseId": 226,
    "inverseCardinality": "ONE_TO_MANY",
    "labeledTypes": []
  }, {
    "id": "ticket_note",
    "fromObject": "ticket",
    "toObject": "note",
    "forwardId": 227,
    "forwardCardinality": "ONE_TO_MANY",
    "inverseId": 228,
    "inverseCardinality": "ONE_TO_MANY",
    "labeledTypes": []
  }, {
    "id": "ticket_order",
    "fromObject": "ticket",
    "toObject": "order",
    "forwardId": 526,
    "forwardCardinality": "ONE_TO_MANY",
    "inverseId": 525,
    "inverseCardinality": "ONE_TO_MANY",
    "labeledTypes": []
  }, {
    "id": "ticket_postal_mail",
    "fromObject": "ticket",
    "toObject": "postal_mail",
    "forwardId": 456,
    "forwardCardinality": "ONE_TO_MANY",
    "inverseId": 455,
    "inverseCardinality": "ONE_TO_MANY",
    "labeledTypes": []
  }, {
    "id": "ticket_project",
    "fromObject": "ticket",
    "toObject": "project",
    "forwardId": 1241,
    "forwardCardinality": "ONE_TO_MANY",
    "inverseId": 1240,
    "inverseCardinality": "ONE_TO_MANY",
    "labeledTypes": []
  }, {
    "id": "ticket_service",
    "fromObject": "ticket",
    "toObject": "service",
    "forwardId": 797,
    "forwardCardinality": "ONE_TO_MANY",
    "inverseId": 796,
    "inverseCardinality": "ONE_TO_MANY",
    "labeledTypes": []
  }, {
    "id": "ticket_subscription",
    "fromObject": "ticket",
    "toObject": "subscription",
    "forwardId": 1122,
    "forwardCardinality": "ONE_TO_MANY",
    "inverseId": 1121,
    "inverseCardinality": "ONE_TO_MANY",
    "labeledTypes": []
  }, {
    "id": "ticket_task",
    "fromObject": "ticket",
    "toObject": "task",
    "forwardId": 229,
    "forwardCardinality": "ONE_TO_MANY",
    "inverseId": 230,
    "inverseCardinality": "ONE_TO_MANY",
    "labeledTypes": []
  }, {
    "id": "ticket_ticket",
    "fromObject": "ticket",
    "toObject": "ticket",
    "forwardId": 452,
    "forwardCardinality": "ONE_TO_MANY",
    "inverseId": 452,
    "inverseCardinality": "ONE_TO_MANY",
    "labeledTypes": []
  }]
};

export const AssociationsTable = ({data, objectMetadata, objectType = "contact", showPrimary = true, symbols = true}) => {
  const InfoIcon = () => <svg aria-hidden="true" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32" focusable="false" data-icon-name="InfoCircle" className="info-icon"><path d="M15.86 31.86c-8.82 0-16-7.18-16-16s7.18-16 16-16 16 7.18 16 16-7.18 16-16 16m0-29.72C8.3 2.14 2.15 8.29 2.15 15.85S8.3 29.56 15.86 29.56s13.71-6.15 13.71-13.71S23.42 2.14 15.86 2.14"></path><path d="M15.86 25c-.63 0-1.14-.51-1.14-1.14V13.57a1.14 1.14 0 1 1 2.28 0v10.29c0 .63-.51 1.14-1.14 1.14"></path><circle cx="15.86" cy="8.43" r=".57"></circle><path d="M15.86 10.14a1.71 1.71 0 1 1-.002-3.418 1.71 1.71 0 0 1 .002 3.418m0-2.28a.57.57 0 1 0 0 1.14.57.57 0 0 0 0-1.14M15.86 14.71h-3.43a1.14 1.14 0 1 1 0-2.28h3.43a1.14 1.14 0 1 1 0 2.28M20.43 25h-9.14a1.14 1.14 0 1 1 0-2.28h9.14a1.14 1.14 0 1 1 0 2.28"></path></svg>;
  const OneToManyIcon = () => <svg aria-hidden="true" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32" focusable="false" data-icon-name="ObjectAssociations" className="cardinality-icon" style={{
    display: 'inline-block',
    verticalAlign: 'middle'
  }}>
        <path d="M27.29 31.86c-.29 0-.58-.11-.81-.33a1.14 1.14 0 0 1 0-1.62l2.62-2.62-2.62-2.62a1.14 1.14 0 0 1 0-1.62 1.15 1.15 0 0 1 1.62 0l3.43 3.43c.45.45.45 1.17 0 1.62l-3.43 3.43c-.22.22-.52.33-.81.33"></path><path d="M30.71 28.43H12.43c-.63 0-1.14-.51-1.14-1.14V4.43c0-.63.51-1.14 1.14-1.14h18.29a1.14 1.14 0 1 1 0 2.28H13.57v20.57h17.14a1.14 1.14 0 1 1 0 2.28Z"></path><path d="M27.29 9c-.29 0-.58-.11-.81-.33a1.14 1.14 0 0 1 0-1.62l2.62-2.62-2.62-2.62a1.14 1.14 0 0 1 0-1.62 1.15 1.15 0 0 1 1.62 0l3.43 3.43c.45.45.45 1.17 0 1.62L28.1 8.67c-.22.22-.52.33-.81.33M30.71 17H1a1.14 1.14 0 1 1 0-2.28h29.71a1.14 1.14 0 1 1 0 2.28"></path><path d="M27.29 20.43c-.29 0-.58-.11-.81-.33a1.14 1.14 0 0 1 0-1.62l2.62-2.62-2.62-2.62a1.14 1.14 0 0 1 0-1.62 1.15 1.15 0 0 1 1.62 0l3.43 3.43c.45.45.45 1.17 0 1.62L28.1 20.1c-.22.22-.52.33-.81.33"></path>
      </svg>;
  const OneToOneIcon = () => <svg data-test-id="cardinality-icon-ONE_TO_ONE" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32" focusable="false" data-icon-name="Next" className="cardinality-icon">
      <path d="M30.71 17H1C0.697654 17 0.40769 16.8799 0.193899 16.6661C-0.0198926 16.4523 -0.139999 16.1623 -0.139999 15.86C-0.139999 15.5576 -0.0198926 15.2677 0.193899 15.0539C0.40769 14.8401 0.697654 14.72 1 14.72H30.71C31.0123 14.72 31.3023 14.8401 31.5161 15.0539C31.7299 15.2677 31.85 15.5576 31.85 15.86C31.85 16.1623 31.7299 16.4523 31.5161 16.6661C31.3023 16.8799 31.0123 17 30.71 17Z" fill="#1F1F1F" />
      <path d="M27.29 20.43C27 20.43 26.71 20.32 26.48 20.1C26.373 19.994 26.288 19.8679 26.23 19.7289C26.172 19.5898 26.1422 19.4407 26.1422 19.29C26.1422 19.1394 26.172 18.9903 26.23 18.8512C26.288 18.7122 26.373 18.586 26.48 18.48L29.1 15.86L26.48 13.24C26.373 13.134 26.288 13.0079 26.23 12.8689C26.172 12.7298 26.1422 12.5807 26.1422 12.43C26.1422 12.2794 26.172 12.1303 26.23 11.9912C26.288 11.8522 26.373 11.726 26.48 11.62C26.6954 11.4063 26.9865 11.2864 27.29 11.2864C27.5934 11.2864 27.8846 11.4063 28.1 11.62L31.53 15.05C31.98 15.5 31.98 16.22 31.53 16.67L28.1 20.1C27.88 20.32 27.58 20.43 27.29 20.43Z" fill="#1F1F1F" />
      </svg>;
  const [associations] = useState((data && data.associations || []).filter(a => a.visible !== false));
  const linkableNames = useMemo(() => {
    if (!objectMetadata) return [];
    const names = [];
    Object.entries(objectMetadata).forEach(([key, meta]) => {
      if (meta.linkUrl) {
        names.push({
          name: meta.displayName,
          url: meta.linkUrl
        });
        if (meta.pluralName && meta.pluralName !== meta.displayName) {
          names.push({
            name: meta.pluralName,
            url: meta.linkUrl
          });
        }
      }
    });
    names.sort((a, b) => b.name.length - a.name.length);
    return names;
  }, [objectMetadata]);
  const linkableNamesRegex = useMemo(() => {
    if (!linkableNames.length) return null;
    const pattern = linkableNames.map(item => item.name.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')).join('|');
    return new RegExp(`\\b(${pattern})\\b`, 'gi');
  }, [linkableNames]);
  const linkifyLabel = label => {
    if (!linkableNamesRegex) return label;
    const regex = new RegExp(linkableNamesRegex.source, linkableNamesRegex.flags);
    const parts = [];
    let lastIndex = 0;
    let match;
    while ((match = regex.exec(label)) !== null) {
      if (match.index > lastIndex) {
        parts.push({
          type: 'text',
          content: label.substring(lastIndex, match.index)
        });
      }
      const matchedText = match[0];
      const linkInfo = linkableNames.find(item => item.name.toLowerCase() === matchedText.toLowerCase());
      parts.push({
        type: 'link',
        content: matchedText,
        url: linkInfo?.url
      });
      lastIndex = match.index + matchedText.length;
    }
    if (lastIndex < label.length) {
      parts.push({
        type: 'text',
        content: label.substring(lastIndex)
      });
    }
    if (parts.length === 0) return label;
    return <>
        {parts.map((part, i) => part.type === 'link' ? <a key={i} href={part.url}>{part.content}</a> : part.content)}
      </>;
  };
  const normalizeObjectName = name => {
    const normalized = name.trim().toLowerCase();
    const nameMap = {};
    Object.entries(objectMetadata).forEach(([key, meta]) => {
      if (meta.pluralName) nameMap[meta.pluralName.toLowerCase()] = key;
      if (meta.objectId) nameMap[meta.objectId] = key;
      nameMap[meta.displayName.toLowerCase()] = key;
      if (meta.displayName.includes(' ')) nameMap[meta.displayName.replace(/ /g, '_')] = key;
      if (key.includes('_')) nameMap[key.replace(/_/g, ' ')] = key;
    });
    return nameMap[normalized] || normalized;
  };
  const getObjectTypeArray = () => {
    return objectType.split(',').map(s => normalizeObjectName(s));
  };
  const getFilteredAssociations = () => {
    const objectTypes = getObjectTypeArray();
    return associations.filter(assoc => {
      const fromObj = normalizeObjectName(assoc.fromObject);
      const toObj = normalizeObjectName(assoc.toObject);
      return objectTypes.includes(fromObj) || objectTypes.includes(toObj);
    });
  };
  const getCardinalityIcon = cardinality => {
    if (cardinality === 'ONE_TO_ONE') {
      return <OneToOneIcon />;
    }
    return <OneToManyIcon />;
  };
  const renderAssociationTypeCell = row => {
    const cardinalityLabel = row.cardinality === 'ONE_TO_ONE' ? 'one to one' : 'one to many';
    if (symbols && row.fromObj && row.toObj) {
      const fromLabel = row.isLabeled ? `${row.label} ${row.fromObj}` : row.fromObj;
      return <>
          <span style={{
        verticalAlign: 'middle'
      }}>{fromLabel}</span>
          {' '}
          <span className="associations-table-icon" role="img" aria-label={cardinalityLabel}>{getCardinalityIcon(row.cardinality)}</span>
          {' '}
          <span style={{
        verticalAlign: 'middle'
      }}>{row.toObj}</span>
        </>;
    }
    return <>
        <span style={{
      verticalAlign: 'middle'
    }}>{linkifyLabel(row.label)}</span>
        {' '}
        <span className="associations-table-icon" role="img" aria-label={cardinalityLabel}>{getCardinalityIcon(row.cardinality)}</span>
      </>;
  };
  const cap = s => s.charAt(0).toUpperCase() + s.slice(1);
  const TableHeader = ({title, tooltip}) => <th>
      <Tooltip tip={tooltip}>
        <span className="associations-table-th-label">{title}<InfoIcon /></span>
      </Tooltip>
    </th>;
  const ObjectCell = ({objectKey, rowSpan}) => {
    const meta = objectMetadata?.[objectKey];
    const label = cap(meta?.displayName || objectKey);
    const linkUrl = meta?.linkUrl;
    const objectId = meta?.objectId;
    return <td rowSpan={rowSpan} className="associations-table-object-cell">
        <div className="associations-table-object-name">
          {linkUrl ? <a href={linkUrl}>{label}</a> : label}
        </div>
        {objectId && <div className="associations-table-object-id">
            <code>{objectId}</code>
          </div>}
      </td>;
  };
  const TargetGroupRows = ({targetGroup, targetIndex}) => {
    return targetGroup.allRows.map((row, rowIndex) => <tr key={row.id} className={`${targetIndex % 2 === 0 ? 'even' : 'odd'} ${rowIndex === 0 ? 'group-first' : ''} ${rowIndex === targetGroup.allRows.length - 1 ? 'group-last' : ''}`}>
        {rowIndex === 0 && <ObjectCell objectKey={targetGroup.targetObject} rowSpan={targetGroup.allRows.length} />}
        <td className="associations-table-type-cell">
          {renderAssociationTypeCell(row)}
        </td>
        <td><code>{row.typeId}</code></td>
      </tr>);
  };
  const getAssociationsBySourceObject = () => {
    const objectTypes = getObjectTypeArray();
    const groupedBySource = {};
    objectTypes.forEach(objType => {
      groupedBySource[objType] = [];
    });
    getFilteredAssociations().forEach(assoc => {
      const fromObj = normalizeObjectName(assoc.fromObject);
      const toObj = normalizeObjectName(assoc.toObject);
      const isSelf = fromObj === toObj;
      const fromDisplay = cap(objectMetadata?.[fromObj]?.displayName || fromObj);
      const toDisplay = cap(objectMetadata?.[toObj]?.displayName || toObj);
      const buildRowsForPerspective = (srcObj, tgtObj) => {
        const isSrcFrom = srcObj === fromObj;
        const srcDisplay = cap(objectMetadata?.[srcObj]?.displayName || srcObj);
        const tgtDisplay = cap(objectMetadata?.[tgtObj]?.displayName || tgtObj);
        const rows = [];
        if (isSelf) {
          rows.push({
            id: `${assoc.id}_base`,
            typeId: assoc.forwardId,
            label: `${fromDisplay} to ${fromDisplay}`,
            fromObj: fromDisplay,
            toObj: fromDisplay,
            cardinality: assoc.forwardCardinality,
            targetObject: tgtObj
          });
        } else {
          const fwdId = isSrcFrom ? assoc.forwardId : assoc.inverseId;
          const fwdCardinality = isSrcFrom ? assoc.forwardCardinality : assoc.inverseCardinality;
          const invId = isSrcFrom ? assoc.inverseId : assoc.forwardId;
          const invCardinality = isSrcFrom ? assoc.inverseCardinality : assoc.forwardCardinality;
          rows.push({
            id: `${assoc.id}_base_fwd`,
            typeId: fwdId,
            label: `${srcDisplay} to ${tgtDisplay}`,
            fromObj: srcDisplay,
            toObj: tgtDisplay,
            cardinality: fwdCardinality,
            targetObject: tgtObj
          });
          rows.push({
            id: `${assoc.id}_base_inv`,
            typeId: invId,
            label: `${tgtDisplay} to ${srcDisplay}`,
            fromObj: tgtDisplay,
            toObj: srcDisplay,
            cardinality: invCardinality,
            targetObject: tgtObj
          });
        }
        if (showPrimary) {
          assoc.labeledTypes.forEach(lt => {
            const fwdLabel = lt.label;
            const invLabel = lt.inverseLabel || lt.label;
            if (isSelf) {
              rows.push({
                id: `${assoc.id}_lt_${lt.forwardId}_fwd`,
                typeId: lt.forwardId,
                label: fwdLabel,
                fromObj: fromDisplay,
                toObj: fromDisplay,
                cardinality: lt.forwardCardinality,
                targetObject: tgtObj,
                isPrimary: lt.isPrimary,
                isLabeled: true
              });
              rows.push({
                id: `${assoc.id}_lt_${lt.forwardId}_inv`,
                typeId: lt.inverseId,
                label: invLabel,
                fromObj: fromDisplay,
                toObj: fromDisplay,
                cardinality: lt.inverseCardinality,
                targetObject: tgtObj,
                isPrimary: lt.isPrimary,
                isLabeled: true
              });
            } else {
              const rowFwdLabel = isSrcFrom ? fwdLabel : invLabel;
              const rowFwdId = isSrcFrom ? lt.forwardId : lt.inverseId;
              const rowFwdCardinality = isSrcFrom ? lt.forwardCardinality : lt.inverseCardinality;
              const rowInvLabel = isSrcFrom ? invLabel : fwdLabel;
              const rowInvId = isSrcFrom ? lt.inverseId : lt.forwardId;
              const rowInvCardinality = isSrcFrom ? lt.inverseCardinality : lt.forwardCardinality;
              rows.push({
                id: `${assoc.id}_lt_${lt.forwardId}_fwd`,
                typeId: rowFwdId,
                label: rowFwdLabel,
                fromObj: isSrcFrom ? srcDisplay : tgtDisplay,
                toObj: isSrcFrom ? tgtDisplay : srcDisplay,
                cardinality: rowFwdCardinality,
                targetObject: tgtObj,
                isPrimary: lt.isPrimary,
                isLabeled: true
              });
              rows.push({
                id: `${assoc.id}_lt_${lt.forwardId}_inv`,
                typeId: rowInvId,
                label: rowInvLabel,
                fromObj: isSrcFrom ? tgtDisplay : srcDisplay,
                toObj: isSrcFrom ? srcDisplay : tgtDisplay,
                cardinality: rowInvCardinality,
                targetObject: tgtObj,
                isPrimary: lt.isPrimary,
                isLabeled: true
              });
            }
          });
        }
        return rows;
      };
      if (objectTypes.includes(fromObj)) {
        const rows = buildRowsForPerspective(fromObj, toObj);
        groupedBySource[fromObj].push({
          id: `${assoc.id}_from`,
          rows,
          targetObject: toObj
        });
      }
      if (objectTypes.includes(toObj) && !isSelf) {
        const rows = buildRowsForPerspective(toObj, fromObj);
        groupedBySource[toObj].push({
          id: `${assoc.id}_to`,
          rows,
          targetObject: fromObj
        });
      }
    });
    return Object.entries(groupedBySource).filter(([_, groups]) => groups.length > 0).map(([objType, groups]) => ({
      objectType: objType,
      groups
    }));
  };
  const mergeGroupsByTargetObject = sourceGroups => {
    return sourceGroups.map(sourceGroup => {
      const targetMap = {};
      sourceGroup.groups.forEach(group => {
        const targetObj = group.targetObject;
        if (!targetMap[targetObj]) {
          targetMap[targetObj] = {
            targetObject: targetObj,
            allRows: []
          };
        }
        targetMap[targetObj].allRows.push(...group.rows);
      });
      return {
        objectType: sourceGroup.objectType,
        targetGroups: Object.values(targetMap).sort((a, b) => {
          const aLabel = objectMetadata?.[a.targetObject]?.displayName || a.targetObject;
          const bLabel = objectMetadata?.[b.targetObject]?.displayName || b.targetObject;
          return aLabel.localeCompare(bLabel, 'en', {
            sensitivity: 'base'
          });
        })
      };
    });
  };
  const sourceObjectGroups = mergeGroupsByTargetObject(getAssociationsBySourceObject());
  return <div className="associations-table">
      {sourceObjectGroups.map((sourceGroup, tableIndex) => <div key={sourceGroup.objectType} className="associations-table-section">
          {tableIndex > 0 && <div className="associations-table-section-spacer"></div>}

          <div className="associations-table-container">
            <table className="associations-table-data">
              <thead>
                <tr>
                  <TableHeader title="Object" tooltip="The object type and its ID." />
                  <TableHeader title="Association type" tooltip={<div>
                        <p>The direction and label of the association, with arrows indicating the type of relationship.</p>
                        <div><OneToOneIcon /> : one-to-one</div>
                        <div><OneToManyIcon /> : one-to-many</div>
                      </div>} />
                  <TableHeader title="Type ID" tooltip="The `associationTypeId` value to include when making associations API requests. Use `HUBSPOT_DEFINED` as the `associationCategory` for these IDs." cta="See Associations API" href="" />
                </tr>
              </thead>
              <tbody>
                {sourceGroup.targetGroups.length === 0 ? <tr>
                    <td colSpan="3" className="associations-table-empty">
                      No associations found
                    </td>
                  </tr> : sourceGroup.targetGroups.map((targetGroup, targetIndex) => <TargetGroupRows key={targetGroup.targetObject} targetGroup={targetGroup} targetIndex={targetIndex} />)}
              </tbody>
            </table>
          </div>
        </div>)}
    </div>;
};

export const CopyCode = ({children, className = ''}) => {
  const idRef = useRef(`copy-code-${Math.random().toString(36).slice(2)}`);
  const statusId = `${idRef.current}-status`;
  const [isCopied, setIsCopied] = useState(false);
  const getTextContent = el => {
    if (typeof el === 'string' || typeof el === 'number') return String(el);
    if (el?.props) return el.props.children || '';
    if (Array.isArray(el)) return el.map(getTextContent).join('');
    return String(el);
  };
  const handleCopy = async () => {
    try {
      await navigator.clipboard.writeText(getTextContent(children));
      setIsCopied(true);
      const status = document.getElementById(statusId);
      if (status) status.textContent = 'Code copied to clipboard';
      setTimeout(() => {
        setIsCopied(false);
        const s = document.getElementById(statusId);
        if (s) s.textContent = '';
      }, 1500);
    } catch {
      const status = document.getElementById(statusId);
      if (status) status.textContent = 'Failed to copy code';
    }
  };
  return <span id={idRef.current} className={`copy-code${className ? ` ${className}` : ''}`}>
      <span className="code-text" onClick={handleCopy} onKeyDown={e => {
    if (e.key === 'Enter' || e.key === ' ') {
      e.preventDefault();
      handleCopy();
    }
  }} style={{
    cursor: 'pointer'
  }} role="button" tabIndex={0} aria-label={isCopied ? 'Code copied to clipboard' : 'Copy code to clipboard'}>
        {children}
        {isCopied ? <svg width="11" height="11" viewBox="0 0 14 14" fill="none" xmlns="http://www.w3.org/2000/svg" aria-hidden="true" className="copy-success-icon" style={{
    marginLeft: '0px',
    verticalAlign: 'middle',
    display: 'inline-block'
  }}>
            <path d="M11.6666 3.5L5.24992 9.91667L2.33325 7" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" />
          </svg> : <Icon icon="copy" size="11" iconType="regular" aria-hidden="true" />}
      </span>
      <div id={statusId} className="sr-only" role="status" aria-live="polite" aria-atomic="true" />
    </span>;
};

export const Dropdown = ({value, onChange, onClick, open, onOpenChange, options, allLabel, buttonStyle = 'button', className}) => {
  const triggerLabel = value === 'all' ? allLabel ?? 'Select' : options.find(o => o.value === value)?.label ?? value;
  const handleSelect = (selectedValue, e) => {
    if (onClick) onClick(selectedValue, e);
    onChange(selectedValue);
    onOpenChange(false);
  };
  return <div className={`dropdown${className ? ` ${className}` : ''}`}>
      <button type="button" className={`dropdown-trigger dropdown-trigger--${buttonStyle}`} onClick={() => onOpenChange(!open)} onKeyDown={e => {
    if (e.key === 'Escape' && open) {
      e.preventDefault();
      onOpenChange(false);
    } else if (e.key === 'ArrowDown') {
      e.preventDefault();
      if (!open) onOpenChange(true); else e.currentTarget.nextElementSibling?.querySelector('button')?.focus();
    } else if (e.key === 'ArrowUp' && open) {
      e.preventDefault();
      const opts = e.currentTarget.nextElementSibling?.querySelectorAll('button');
      opts?.[opts.length - 1]?.focus();
    }
  }} aria-expanded={open} aria-haspopup="listbox">
        <span>{triggerLabel}</span>
        <span className="dropdown-caret" aria-hidden="true" />
      </button>
      {open && <div className="dropdown-menu" role="listbox">
          {allLabel && <button type="button" className={`dropdown-option${value === 'all' ? ' dropdown-option--selected' : ''}`} onClick={e => handleSelect('all', e)} onKeyDown={e => handleOptionKeyDown(e, () => onOpenChange(false))} role="option" aria-selected={value === 'all'}>
              {allLabel}
            </button>}
          {options.map(option => <button key={option.value} type="button" className={`dropdown-option${value === option.value ? ' dropdown-option--selected' : ''}`} onClick={e => handleSelect(option.value, e)} onKeyDown={e => handleOptionKeyDown(e, () => onOpenChange(false))} role="option" aria-selected={value === option.value}>
              {option.label}
            </button>)}
        </div>}
    </div>;
};

export const defaultCourseProperties = {
  "properties": [{
    "name": "hs_course_description",
    "label": "Course Description",
    "type": "string",
    "fieldType": "text",
    "json": {
      "name": "hs_course_description",
      "label": "Course Description",
      "type": "string",
      "fieldType": "text",
      "description": "",
      "groupName": "course_information",
      "options": [],
      "displayOrder": -1,
      "calculated": false,
      "externalOptions": false,
      "hasUniqueValue": false,
      "hidden": false,
      "hubspotDefined": true,
      "modificationMetadata": {
        "archivable": true,
        "readOnlyDefinition": false,
        "readOnlyValue": false
      },
      "formField": false,
      "dataSensitivity": "non_sensitive"
    }
  }, {
    "name": "hs_course_id",
    "label": "Course ID",
    "type": "string",
    "fieldType": "text",
    "secondaryDisplayProperty": true,
    "searchable": true,
    "json": {
      "name": "hs_course_id",
      "label": "Course ID",
      "type": "string",
      "fieldType": "text",
      "description": "",
      "groupName": "course_information",
      "options": [],
      "displayOrder": -1,
      "calculated": false,
      "externalOptions": false,
      "hasUniqueValue": true,
      "hidden": false,
      "hubspotDefined": true,
      "modificationMetadata": {
        "archivable": true,
        "readOnlyDefinition": false,
        "readOnlyValue": false
      },
      "formField": false,
      "dataSensitivity": "non_sensitive"
    }
  }, {
    "name": "hs_course_name",
    "label": "Course Name",
    "type": "string",
    "fieldType": "text",
    "required": true,
    "primaryDisplayProperty": true,
    "searchable": true,
    "json": {
      "name": "hs_course_name",
      "label": "Course Name",
      "type": "string",
      "fieldType": "text",
      "description": "",
      "groupName": "course_information",
      "options": [],
      "displayOrder": -1,
      "calculated": false,
      "externalOptions": false,
      "hasUniqueValue": false,
      "hidden": false,
      "hubspotDefined": true,
      "modificationMetadata": {
        "archivable": true,
        "readOnlyDefinition": false,
        "readOnlyValue": false
      },
      "formField": false,
      "dataSensitivity": "non_sensitive"
    }
  }, {
    "name": "hs_created_by_user_id",
    "label": "Created by user ID",
    "type": "number",
    "fieldType": "number",
    "description": "The user who created this record. This value is set automatically by HubSpot.",
    "json": {
      "name": "hs_created_by_user_id",
      "label": "Created by user ID",
      "type": "number",
      "fieldType": "number",
      "description": "The user who created this record. This value is set automatically by HubSpot.",
      "groupName": "course_information",
      "options": [],
      "displayOrder": -1,
      "calculated": false,
      "externalOptions": true,
      "hasUniqueValue": false,
      "hidden": false,
      "hubspotDefined": true,
      "modificationMetadata": {
        "archivable": true,
        "readOnlyDefinition": true,
        "readOnlyValue": true
      },
      "formField": false,
      "dataSensitivity": "non_sensitive"
    }
  }, {
    "name": "hs_createdate",
    "label": "Object create date/time",
    "type": "datetime",
    "fieldType": "date",
    "description": "The date and time at which this object was created. This value is automatically set by HubSpot and may not be modified.",
    "json": {
      "name": "hs_createdate",
      "label": "Object create date/time",
      "type": "datetime",
      "fieldType": "date",
      "description": "The date and time at which this object was created. This value is automatically set by HubSpot and may not be modified.",
      "groupName": "course_information",
      "options": [],
      "displayOrder": -1,
      "calculated": false,
      "externalOptions": false,
      "hasUniqueValue": false,
      "hidden": false,
      "hubspotDefined": true,
      "modificationMetadata": {
        "archivable": true,
        "readOnlyDefinition": true,
        "readOnlyValue": true
      },
      "formField": false,
      "dataSensitivity": "non_sensitive"
    }
  }, {
    "name": "hs_enrollment_capacity",
    "label": "Enrollment Capacity",
    "type": "number",
    "fieldType": "number",
    "json": {
      "name": "hs_enrollment_capacity",
      "label": "Enrollment Capacity",
      "type": "number",
      "fieldType": "number",
      "description": "",
      "groupName": "course_information",
      "options": [],
      "displayOrder": -1,
      "calculated": false,
      "externalOptions": false,
      "hasUniqueValue": false,
      "hidden": false,
      "hubspotDefined": true,
      "modificationMetadata": {
        "archivable": true,
        "readOnlyDefinition": false,
        "readOnlyValue": false
      },
      "formField": false,
      "dataSensitivity": "non_sensitive"
    }
  }, {
    "name": "hs_lastmodifieddate",
    "label": "Object last modified date/time",
    "type": "datetime",
    "fieldType": "date",
    "description": "Most recent timestamp of any property update for this object. This includes HubSpot internal properties, which can be visible or hidden. This property is updated automatically.",
    "json": {
      "name": "hs_lastmodifieddate",
      "label": "Object last modified date/time",
      "type": "datetime",
      "fieldType": "date",
      "description": "Most recent timestamp of any property update for this object. This includes HubSpot internal properties, which can be visible or hidden. This property is updated automatically.",
      "groupName": "course_information",
      "options": [],
      "displayOrder": -1,
      "calculated": false,
      "externalOptions": false,
      "hasUniqueValue": false,
      "hidden": false,
      "hubspotDefined": true,
      "modificationMetadata": {
        "archivable": true,
        "readOnlyDefinition": true,
        "readOnlyValue": true
      },
      "formField": false,
      "dataSensitivity": "non_sensitive"
    }
  }, {
    "name": "hs_merged_object_ids",
    "label": "Merged record IDs",
    "type": "enumeration",
    "fieldType": "checkbox",
    "description": "The list of record IDs that have been merged into this record. This value is set automatically by HubSpot.",
    "json": {
      "name": "hs_merged_object_ids",
      "label": "Merged record IDs",
      "type": "enumeration",
      "fieldType": "checkbox",
      "description": "The list of record IDs that have been merged into this record. This value is set automatically by HubSpot.",
      "groupName": "course_information",
      "options": [],
      "displayOrder": -1,
      "calculated": false,
      "externalOptions": true,
      "hasUniqueValue": false,
      "hidden": false,
      "hubspotDefined": true,
      "modificationMetadata": {
        "archivable": true,
        "readOnlyDefinition": true,
        "readOnlyValue": true
      },
      "formField": false,
      "dataSensitivity": "non_sensitive"
    }
  }, {
    "name": "hs_object_id",
    "label": "Record ID",
    "type": "number",
    "fieldType": "number",
    "description": "The unique ID for this record. This value is set automatically by HubSpot.",
    "json": {
      "name": "hs_object_id",
      "label": "Record ID",
      "type": "number",
      "fieldType": "number",
      "description": "The unique ID for this record. This value is set automatically by HubSpot.",
      "groupName": "course_information",
      "options": [],
      "displayOrder": -1,
      "calculated": false,
      "externalOptions": false,
      "hasUniqueValue": false,
      "hidden": false,
      "hubspotDefined": true,
      "modificationMetadata": {
        "archivable": true,
        "readOnlyDefinition": true,
        "readOnlyValue": true
      },
      "formField": false,
      "dataSensitivity": "non_sensitive"
    }
  }, {
    "name": "hs_object_source_detail_1",
    "label": "Record source detail 1",
    "type": "string",
    "fieldType": "text",
    "description": "First level of detail on how this record was created.",
    "json": {
      "name": "hs_object_source_detail_1",
      "label": "Record source detail 1",
      "type": "string",
      "fieldType": "text",
      "description": "First level of detail on how this record was created.",
      "groupName": "course_information",
      "options": [],
      "displayOrder": -1,
      "calculated": false,
      "externalOptions": false,
      "hasUniqueValue": false,
      "hidden": false,
      "hubspotDefined": true,
      "modificationMetadata": {
        "archivable": true,
        "readOnlyDefinition": true,
        "readOnlyValue": true
      },
      "formField": false,
      "dataSensitivity": "non_sensitive"
    }
  }, {
    "name": "hs_object_source_detail_2",
    "label": "Record source detail 2",
    "type": "string",
    "fieldType": "text",
    "description": "Second level of detail on how this record was created.",
    "json": {
      "name": "hs_object_source_detail_2",
      "label": "Record source detail 2",
      "type": "string",
      "fieldType": "text",
      "description": "Second level of detail on how this record was created.",
      "groupName": "course_information",
      "options": [],
      "displayOrder": -1,
      "calculated": false,
      "externalOptions": false,
      "hasUniqueValue": false,
      "hidden": false,
      "hubspotDefined": true,
      "modificationMetadata": {
        "archivable": true,
        "readOnlyDefinition": true,
        "readOnlyValue": true
      },
      "formField": false,
      "dataSensitivity": "non_sensitive"
    }
  }, {
    "name": "hs_object_source_detail_3",
    "label": "Record source detail 3",
    "type": "string",
    "fieldType": "text",
    "description": "Third level of detail on how this record was created.",
    "json": {
      "name": "hs_object_source_detail_3",
      "label": "Record source detail 3",
      "type": "string",
      "fieldType": "text",
      "description": "Third level of detail on how this record was created.",
      "groupName": "course_information",
      "options": [],
      "displayOrder": -1,
      "calculated": false,
      "externalOptions": false,
      "hasUniqueValue": false,
      "hidden": false,
      "hubspotDefined": true,
      "modificationMetadata": {
        "archivable": true,
        "readOnlyDefinition": true,
        "readOnlyValue": true
      },
      "formField": false,
      "dataSensitivity": "non_sensitive"
    }
  }, {
    "name": "hs_object_source_label",
    "label": "Record source",
    "type": "enumeration",
    "fieldType": "select",
    "description": "How this record was created.",
    "json": {
      "name": "hs_object_source_label",
      "label": "Record source",
      "type": "enumeration",
      "fieldType": "select",
      "description": "How this record was created.",
      "groupName": "course_information",
      "options": [],
      "displayOrder": -1,
      "calculated": false,
      "externalOptions": true,
      "hasUniqueValue": false,
      "hidden": false,
      "hubspotDefined": true,
      "modificationMetadata": {
        "archivable": true,
        "readOnlyDefinition": true,
        "readOnlyValue": true
      },
      "formField": false,
      "dataSensitivity": "non_sensitive"
    }
  }, {
    "name": "hs_pipeline",
    "label": "Pipeline",
    "type": "enumeration",
    "fieldType": "select",
    "description": "The pipeline that contains this course",
    "json": {
      "name": "hs_pipeline",
      "label": "Pipeline",
      "type": "enumeration",
      "fieldType": "select",
      "description": "The pipeline that contains this course",
      "groupName": "course_information",
      "options": [],
      "displayOrder": -1,
      "calculated": false,
      "externalOptions": true,
      "hasUniqueValue": false,
      "hidden": false,
      "hubspotDefined": true,
      "modificationMetadata": {
        "archivable": true,
        "readOnlyDefinition": true,
        "readOnlyValue": false
      },
      "formField": false,
      "dataSensitivity": "non_sensitive"
    }
  }, {
    "name": "hs_pipeline_stage",
    "label": "Pipeline Stage",
    "type": "enumeration",
    "fieldType": "select",
    "description": "The pipeline stage that contains this course",
    "json": {
      "name": "hs_pipeline_stage",
      "label": "Pipeline Stage",
      "type": "enumeration",
      "fieldType": "select",
      "description": "The pipeline stage that contains this course",
      "groupName": "course_information",
      "options": [],
      "displayOrder": -1,
      "calculated": false,
      "externalOptions": true,
      "hasUniqueValue": false,
      "hidden": false,
      "hubspotDefined": true,
      "modificationMetadata": {
        "archivable": true,
        "readOnlyDefinition": true,
        "readOnlyValue": false
      },
      "formField": false,
      "dataSensitivity": "non_sensitive"
    }
  }, {
    "name": "hs_shared_team_ids",
    "label": "Shared teams",
    "type": "enumeration",
    "fieldType": "checkbox",
    "description": "Additional teams whose users can access the record based on their permissions. This can be set manually or through Workflows or APIs.",
    "json": {
      "name": "hs_shared_team_ids",
      "label": "Shared teams",
      "type": "enumeration",
      "fieldType": "checkbox",
      "description": "Additional teams whose users can access the record based on their permissions. This can be set manually or through Workflows or APIs.",
      "groupName": "course_information",
      "options": [],
      "displayOrder": -1,
      "calculated": false,
      "externalOptions": true,
      "hasUniqueValue": false,
      "hidden": false,
      "hubspotDefined": true,
      "modificationMetadata": {
        "archivable": true,
        "readOnlyDefinition": true,
        "readOnlyValue": false
      },
      "formField": false,
      "dataSensitivity": "non_sensitive"
    }
  }, {
    "name": "hs_shared_user_ids",
    "label": "Shared users",
    "type": "enumeration",
    "fieldType": "checkbox",
    "description": "Additional users that can access the record based on their permissions. This can be set manually or through Workflows and APIs.",
    "json": {
      "name": "hs_shared_user_ids",
      "label": "Shared users",
      "type": "enumeration",
      "fieldType": "checkbox",
      "description": "Additional users that can access the record based on their permissions. This can be set manually or through Workflows and APIs.",
      "groupName": "course_information",
      "options": [],
      "displayOrder": -1,
      "calculated": false,
      "externalOptions": true,
      "hasUniqueValue": false,
      "hidden": false,
      "hubspotDefined": true,
      "modificationMetadata": {
        "archivable": true,
        "readOnlyDefinition": true,
        "readOnlyValue": false
      },
      "formField": false,
      "dataSensitivity": "non_sensitive"
    }
  }, {
    "name": "hs_updated_by_user_id",
    "label": "Updated by user ID",
    "type": "number",
    "fieldType": "number",
    "description": "The user who last updated this record. This value is set automatically by HubSpot.",
    "json": {
      "name": "hs_updated_by_user_id",
      "label": "Updated by user ID",
      "type": "number",
      "fieldType": "number",
      "description": "The user who last updated this record. This value is set automatically by HubSpot.",
      "groupName": "course_information",
      "options": [],
      "displayOrder": -1,
      "calculated": false,
      "externalOptions": true,
      "hasUniqueValue": false,
      "hidden": false,
      "hubspotDefined": true,
      "modificationMetadata": {
        "archivable": true,
        "readOnlyDefinition": true,
        "readOnlyValue": true
      },
      "formField": false,
      "dataSensitivity": "non_sensitive"
    }
  }, {
    "name": "hubspot_owner_assigneddate",
    "label": "Owner assigned date",
    "type": "datetime",
    "fieldType": "date",
    "description": "The most recent timestamp of when an owner was assigned to this record. This value is set automatically by HubSpot.",
    "json": {
      "name": "hubspot_owner_assigneddate",
      "label": "Owner assigned date",
      "type": "datetime",
      "fieldType": "date",
      "description": "The most recent timestamp of when an owner was assigned to this record. This value is set automatically by HubSpot.",
      "groupName": "course_information",
      "options": [],
      "displayOrder": -1,
      "calculated": false,
      "externalOptions": false,
      "hasUniqueValue": false,
      "hidden": false,
      "hubspotDefined": true,
      "modificationMetadata": {
        "archivable": true,
        "readOnlyDefinition": true,
        "readOnlyValue": true
      },
      "formField": false,
      "dataSensitivity": "non_sensitive"
    }
  }, {
    "name": "hubspot_owner_id",
    "label": "Owner",
    "type": "enumeration",
    "fieldType": "select",
    "description": "The owner of the object.",
    "json": {
      "name": "hubspot_owner_id",
      "label": "Owner",
      "type": "enumeration",
      "fieldType": "select",
      "description": "The owner of the object.",
      "groupName": "course_information",
      "options": [],
      "referencedObjectType": "OWNER",
      "displayOrder": -1,
      "calculated": false,
      "externalOptions": true,
      "hasUniqueValue": false,
      "hidden": false,
      "hubspotDefined": true,
      "modificationMetadata": {
        "archivable": true,
        "readOnlyDefinition": true,
        "readOnlyValue": false
      },
      "formField": false,
      "dataSensitivity": "non_sensitive"
    }
  }, {
    "name": "hubspot_team_id",
    "label": "Owner's main team",
    "type": "enumeration",
    "fieldType": "select",
    "description": "The main team of the record owner. This value is set automatically by HubSpot.",
    "json": {
      "name": "hubspot_team_id",
      "label": "Owner's main team",
      "type": "enumeration",
      "fieldType": "select",
      "description": "The main team of the record owner. This value is set automatically by HubSpot.",
      "groupName": "course_information",
      "options": [],
      "displayOrder": -1,
      "calculated": false,
      "externalOptions": true,
      "hasUniqueValue": false,
      "hidden": false,
      "hubspotDefined": true,
      "modificationMetadata": {
        "archivable": true,
        "readOnlyDefinition": true,
        "readOnlyValue": true
      },
      "formField": false,
      "dataSensitivity": "non_sensitive"
    }
  }]
};

export const PropertyRow = ({property, fieldTypeLabel, isOpen, onToggle, onCopyLink, hoveredProperty, selectedProperty, visibleJson, onSetHovered, onSetVisibleJson}) => {
  const formatDataType = type => {
    const formatted = type.replace(/_/g, ' ');
    return formatted.charAt(0).toUpperCase() + formatted.slice(1);
  };
  const isHighlighted = selectedProperty === property.name || hoveredProperty === property.name;
  return <>
      <dt id={property.name} className="property-definition-term" onMouseEnter={() => onSetHovered(property.name)} onMouseLeave={() => onSetHovered(null)}>
        <div className={`property-link-icon icon icon--link ${isHighlighted ? 'selected' : ''}`} onClick={e => {
    e.stopPropagation();
    onCopyLink(property.name);
  }} role="button" tabIndex={0} aria-label={`Copy link to ${property.name} property`} onKeyDown={e => {
    if (e.key === 'Enter' || e.key === ' ') {
      e.preventDefault();
      e.stopPropagation();
      onCopyLink(property.name);
    }
  }} />

        <div className={`property-chevron icon icon--chevron ${isOpen ? 'open' : ''}`} onClick={() => onToggle(property.name)} role="button" tabIndex={0} aria-label={`${isOpen ? 'Collapse' : 'Expand'} ${property.name} property`} onKeyDown={e => {
    if (e.key === 'Enter' || e.key === ' ') {
      e.preventDefault();
      onToggle(property.name);
    }
  }} />

        <div className="property-definition-content">
          <button onClick={() => onToggle(property.name)} aria-expanded={isOpen} type="button" className="property-label-button" aria-label={`${isOpen ? 'Collapse' : 'Expand'} ${property.label}`}>
            <span className="property-label">{property.label}</span>
          </button>
          <CopyCode><code>{property.name}</code></CopyCode>
          {property.json && isOpen && <button onClick={e => {
    e.stopPropagation();
    onSetVisibleJson(property.name);
  }} className={`view-json-button ${isHighlighted ? 'selected' : ''}`} type="button" aria-expanded={visibleJson[property.name] || false} style={{
    marginLeft: 'auto'
  }}>
              <Icon icon="code" size="14" iconType="regular" aria-hidden="true" /> {visibleJson[property.name] ? 'Hide JSON' : 'Show JSON'}
            </button>}
        </div>
      </dt>

      {isOpen && <dd className="property-definition-description" onMouseEnter={() => onSetHovered(property.name)} onMouseLeave={() => onSetHovered(null)}>
          <div className="property-definition-row">
            <span className="property-data-type" style={{
    backgroundColor: 'unset'
  }}>
              <strong>Data type: </strong> {formatDataType(property.type)}
            </span>
            <span className="property-field-type">
              <strong>Display type:</strong>
              <span> {fieldTypeLabel}</span>
            </span>
          </div>

          <div className="property-definition-row property-description-text">
            {property.description}
          </div>

          {property.options?.length > 0 && <div className="property-definition-row">
              <OptionsList options={property.options} />
            </div>}

          {property.json && visibleJson[property.name] && <div className="property-json-viewer" style={{
    marginTop: '1rem'
  }}>
              <CodeBlock language="json">
                {JSON.stringify(property.json, null, 2)}
              </CodeBlock>
            </div>}
        </dd>}
    </>;
};

export const OptionsList = ({options}) => {
  const list = <ul className="property-options-list">
      {options.map((option, idx) => <li key={idx}>{option.label} (<code>{option.value}</code>)</li>)}
    </ul>;
  return options.length > 10 ? <Expandable title={`${options.length} options`}>{list}</Expandable> : list;
};

export const DefaultPropertyListing = ({data, placeholder = 'Search properties'}) => {
  const FIELD_TYPE_LABELS = {
    text: 'Single-line text',
    textarea: 'Multi-line text',
    phonenumber: 'Phone number',
    booleancheckbox: 'Single checkbox',
    checkbox: 'Multiple checkboxes',
    select: 'Dropdown select',
    radio: 'Radio select',
    date: 'Date picker',
    datetime: 'Date and time picker',
    number: 'Number',
    calculation_equation: 'Equation',
    calculation_rollup: 'Rollup'
  };
  const formatDataType = type => {
    const formatted = type.replace(/_/g, ' ');
    return formatted.charAt(0).toUpperCase() + formatted.slice(1);
  };
  const properties = data?.properties || [];
  const [mounted, setMounted] = useState(false);
  const [searchTerm, setSearchTerm] = useState('');
  const [debouncedSearchTerm, setDebouncedSearchTerm] = useState('');
  const [openStates, setOpenStates] = useState({});
  const [selectedType, setSelectedType] = useState('all');
  const [selectedFieldType, setSelectedFieldType] = useState('all');
  const [activeDropdown, setActiveDropdown] = useState(null);
  const [selectedProperty, setSelectedProperty] = useState(null);
  const [hoveredProperty, setHoveredProperty] = useState(null);
  const [visibleJson, setVisibleJson] = useState({});
  const listRef = useRef(null);
  const allExpanded = properties.every(p => openStates[p.name] !== false);
  useEffect(() => setMounted(true), []);
  useEffect(() => {
    if (!mounted || !window.location.hash) return;
    const targetId = window.location.hash.substring(1);
    setTimeout(() => {
      const el = document.getElementById(targetId);
      if (el) {
        el.scrollIntoView({
          behavior: 'smooth',
          block: 'start'
        });
        setSelectedProperty(targetId);
      }
    }, 100);
  }, [mounted]);
  useEffect(() => {
    if (!mounted) return;
    const timer = setTimeout(() => setDebouncedSearchTerm(searchTerm), 300);
    return () => clearTimeout(timer);
  }, [searchTerm, mounted]);
  useEffect(() => {
    if (!mounted) return;
    const handleClickOutside = e => {
      if (!e.target.closest('.dropdown')) setActiveDropdown(null);
    };
    document.addEventListener('click', handleClickOutside);
    return () => document.removeEventListener('click', handleClickOutside);
  }, [mounted]);
  const availableTypes = useMemo(() => [...new Set(properties.filter(p => selectedFieldType === 'all' || FIELD_TYPE_LABELS[p.fieldType] === selectedFieldType).map(p => p.type).filter(Boolean))].sort(), [properties, selectedFieldType]);
  const availableFieldTypes = useMemo(() => [...new Set(properties.filter(p => selectedType === 'all' || p.type === selectedType).map(p => FIELD_TYPE_LABELS[p.fieldType]).filter(Boolean))].sort(), [properties, selectedType]);
  const filteredProperties = useMemo(() => {
    if (!mounted) return [];
    const searchLower = debouncedSearchTerm.toLowerCase();
    return properties.filter(prop => {
      const matchesSearch = !debouncedSearchTerm || (prop.name?.toLowerCase().includes(searchLower) || prop.label?.toLowerCase().includes(searchLower) || prop.description?.toLowerCase().includes(searchLower));
      const matchesType = selectedType === 'all' || prop.type === selectedType;
      const matchesFieldType = selectedFieldType === 'all' || FIELD_TYPE_LABELS[prop.fieldType] === selectedFieldType;
      return matchesSearch && matchesType && matchesFieldType;
    });
  }, [properties, debouncedSearchTerm, selectedType, selectedFieldType, mounted]);
  const toggleProperty = useCallback(name => {
    setOpenStates(prev => ({
      ...prev,
      [name]: prev[name] !== false ? false : true
    }));
  }, []);
  const toggleAllProperties = useCallback(() => {
    const expand = !allExpanded;
    setOpenStates(Object.fromEntries(properties.map(p => [p.name, expand])));
  }, [allExpanded, properties]);
  const copyAnchorLink = useCallback(async propertyName => {
    const url = `${window.location.origin}${window.location.pathname}#${propertyName}`;
    setSelectedProperty(propertyName);
    try {
      await navigator.clipboard.writeText(url);
      const el = document.getElementById(propertyName);
      if (el) {
        const container = listRef.current;
        if (container) {
          const scrollOffset = el.getBoundingClientRect().top - container.getBoundingClientRect().top + container.scrollTop - 20;
          container.scrollTo({
            top: scrollOffset,
            behavior: 'smooth'
          });
        }
        setTimeout(() => el.focus({
          preventScroll: true
        }), 500);
      }
      history.replaceState(null, null, `#${propertyName}`);
    } catch {}
  }, []);
  const handleSetVisibleJson = useCallback(name => {
    setVisibleJson(prev => ({
      ...prev,
      [name]: !prev[name]
    }));
  }, []);
  if (!mounted) return null;
  return <div id="property-definitions" className="property-definitions-wrapper">
      <div className="property-definitions-search-row">
        <div className="property-definitions-search">
          <label htmlFor="property-search" className="sr-only">{placeholder}</label>
          <input id="property-search" type="text" placeholder={placeholder} value={searchTerm} onChange={e => setSearchTerm(e.target.value)} maxLength={75} className="property-definitions-input" aria-label={placeholder} />
          <span className="property-definitions-input-icon" aria-hidden="true">
            <Icon icon="magnifying-glass" size="16" />
          </span>
        </div>
        <div className="property-definitions-results-count" role="status" aria-live="polite">
          {filteredProperties.length} {filteredProperties.length === 1 ? 'property' : 'properties'}
        </div>
      </div>

      <div className="property-definitions-filter-row">
        <Dropdown className="property-type-filter" value={selectedType} open={activeDropdown === 'type'} onOpenChange={isOpen => setActiveDropdown(isOpen ? 'type' : null)} onChange={v => setSelectedType(v)} allLabel="All data types" options={availableTypes.map(type => ({
    value: type,
    label: formatDataType(type)
  }))} buttonStyle="link" />
        <Dropdown className="property-field-type-filter" value={selectedFieldType} open={activeDropdown === 'fieldType'} onOpenChange={isOpen => setActiveDropdown(isOpen ? 'fieldType' : null)} onChange={v => setSelectedFieldType(v)} allLabel="All display types" options={availableFieldTypes.map(ft => ({
    value: ft,
    label: ft
  }))} buttonStyle="link" />
        <button onClick={toggleAllProperties} className="collapse-all-button" type="button" aria-label={allExpanded ? 'Collapse all properties' : 'Expand all properties'}>
          {allExpanded ? 'Collapse all' : 'Expand all'}
        </button>
      </div>

      <dl className="property-definitions-list" ref={listRef} style={{
    maxHeight: '800px',
    overflowY: 'scroll',
    minHeight: '200px'
  }}>
        {filteredProperties.length > 0 ? filteredProperties.map(property => <div key={property.name} className="property-row">
              <PropertyRow property={property} fieldTypeLabel={FIELD_TYPE_LABELS[property.fieldType] ?? property.fieldType} isOpen={openStates[property.name] !== false} onToggle={toggleProperty} onCopyLink={copyAnchorLink} hoveredProperty={hoveredProperty} selectedProperty={selectedProperty} visibleJson={visibleJson} onSetHovered={setHoveredProperty} onSetVisibleJson={handleSetVisibleJson} />
            </div>) : <div className="property-definitions-empty">
            {!debouncedSearchTerm.trim() && (selectedType !== 'all' || selectedFieldType !== 'all') ? 'No properties found matching the selected filters.' : `No properties found matching "${searchTerm}"`}
          </div>}
      </dl>
    </div>;
};

export const ObjectDefinitionCard = ({objectName, singularName, pluralName, objectId, fullyQualifiedName, allowsCustomProperties, allowsSensitiveProperties, hasPipelines, description, requiredProperties = [], searchableProperties = [], primaryDisplayProperty, secondaryDisplayProperties = [], children}) => {
  const AttributeLabel = ({icon, tooltip, cta, href, label}) => {
    const tooltipProps = {
      tip: tooltip
    };
    if (cta) tooltipProps.cta = cta;
    if (href) tooltipProps.href = href;
    return <Tooltip {...tooltipProps}>
        <div className="object-property-label tooltip underline decoration-dotted decoration-2 underline-offset-4 decoration-gray-400 dark:decoration-gray-500">
          {icon}
          {label}
        </div>
      </Tooltip>;
  };
  const normalizeProperty = prop => {
    if (typeof prop === 'string') {
      return {
        label: prop,
        name: prop
      };
    }
    return prop;
  };
  const PropertyTable = ({properties}) => {
    const propsArray = Array.isArray(properties) ? properties : [properties];
    const filteredProps = propsArray.filter(prop => {
      if (typeof prop === 'string') return prop.trim().length > 0;
      if (typeof prop === 'object' && prop !== null) {
        return prop.label || prop.name;
      }
      return false;
    });
    const handlePropertyClick = (e, propertyName) => {
      e.preventDefault();
      const targetElement = document.getElementById(propertyName);
      if (targetElement) {
        targetElement.scrollIntoView({
          behavior: 'smooth',
          block: 'start'
        });
        window.location.hash = propertyName;
        setTimeout(() => {
          targetElement.focus({
            preventScroll: true
          });
        }, 500);
      } else {
        window.location.hash = propertyName;
        const checkAndScroll = (attempts = 0, maxAttempts = 20) => {
          const element = document.getElementById(propertyName);
          if (element) {
            setTimeout(() => {
              element.scrollIntoView({
                behavior: 'smooth',
                block: 'start'
              });
              setTimeout(() => {
                element.focus({
                  preventScroll: true
                });
              }, 500);
            }, 150);
          } else if (attempts < maxAttempts) {
            setTimeout(() => checkAndScroll(attempts + 1, maxAttempts), 100);
          }
        };
        setTimeout(() => checkAndScroll(), 100);
      }
    };
    if (filteredProps.length === 0) return null;
    return <table className="property-summary-table">
        <caption className="sr-only">Properties list</caption>
        <thead>
          <tr>
            <th>Label</th>
            <th>Name</th>
          </tr>
        </thead>
        <tbody>
          {filteredProps.map((prop, idx) => {
      const {label, name} = normalizeProperty(prop);
      return <tr key={name || idx}>
                <td>
                  <a href={`#${name}`} className="property-table-link" onClick={e => handlePropertyClick(e, name)}>
                    {label}
                  </a>
                </td>
                <td>
                  <code>{name}</code>
                </td>
              </tr>;
    })}
        </tbody>
      </table>;
  };
  return <div className="object-definition-card-container rounded-2xl border border-gray-950/10 dark:border-white/10 w-full" data-object-type={singularName} data-object-id={objectId} data-allows-custom-properties={allowsCustomProperties} data-allows-sensitive-properties={allowsSensitiveProperties} data-has-pipelines={hasPipelines} role="region" aria-label={`${objectName} object summary`}>
      <div className="object-definition-header" data-section="header">
        <div className="object-definition-title-row">
          <strong className="object-definition-name">{objectName}</strong>
          <p className="object-definition-description">{description}</p>
        </div>
        <div className="summary-row header-row" data-section="identification">
          <div data-property="object-id">
            <strong>Object ID:</strong>
            <code>{objectId}</code>
          </div>
          <div data-property="fully-qualified-name">
            <strong>Fully qualified name (FQN):</strong>
            <code>{fullyQualifiedName}</code>
          </div>
        </div>
      </div>
      <div className="object-definition-tabs">
        <Tabs>
          <Tab title="Overview">
            <div className="object-definition-overview">
              <strong className="object-definition-section-heading">Features</strong>
              <div className="summary-row" data-section="capabilities" role="list" aria-label="Object capabilities">
                {hasPipelines !== undefined && <div data-property="pipelines" data-value={hasPipelines} role="listitem">
                    {hasPipelines ? <span className="prop-icon prop-icon--check" aria-hidden="true" /> : <span className="prop-icon prop-icon--x" aria-hidden="true" />}
                    <span>{hasPipelines ? 'Has' : "Doesn't have"} pipelines</span>
                  </div>}
                <div data-property="custom-properties" data-value={allowsCustomProperties} role="listitem">
                  {allowsCustomProperties ? <span className="prop-icon prop-icon--check" aria-hidden="true" /> : <span className="prop-icon prop-icon--x" aria-hidden="true" />}
                  <span>{allowsCustomProperties ? 'Allows' : "Doesn't allow"} custom properties</span>
                </div>
                <div data-property="sensitive-properties" data-value={allowsSensitiveProperties} role="listitem">
                  {allowsSensitiveProperties ? <span className="prop-icon prop-icon--check" aria-hidden="true" /> : <span className="prop-icon prop-icon--x" aria-hidden="true" />}
                  <span>{allowsSensitiveProperties ? 'Allows' : "Doesn't allow"} sensitive properties</span>
                </div>
              </div>

              <strong className="object-definition-section-heading">Key properties</strong>
              <section className="object-properties-list" data-section="details" aria-label="Key properties">
                <AttributeLabel icon={<span className="prop-icon prop-icon--required" aria-hidden="true" />} tooltip={`Properties required for creating a ${singularName.toLowerCase()}.`} label="Required properties" />
                <div className="object-property-value">
                  {requiredProperties.length > 0 ? <PropertyTable properties={requiredProperties} /> : 'None'}
                </div>

                <AttributeLabel icon={<span className="prop-icon prop-icon--display-property" aria-hidden="true" />} tooltip={`The main display label in the UI for ${pluralName.toLowerCase()}.`} label="Primary display property" />
                <div className="object-property-value">
                  <PropertyTable properties={primaryDisplayProperty} />
                </div>

                <AttributeLabel icon={<span className="prop-icon prop-icon--display-property" aria-hidden="true" />} tooltip={`Additional properties that are used as labels in the UI for ${pluralName.toLowerCase()}.`} label="Secondary display properties" />
                <div className="object-property-value">
                  {secondaryDisplayProperties.length > 0 ? <PropertyTable properties={secondaryDisplayProperties} /> : 'None'}
                </div>

                <AttributeLabel icon={<span className="prop-icon prop-icon--search" aria-hidden="true" />} tooltip={`Properties that can be used to search for ${pluralName.toLowerCase()}.`} cta="Learn more about CRM search" href="/api-reference/search/guide" label="Searchable properties" />
                <div className="object-property-value">
                  {searchableProperties.length > 0 ? <PropertyTable properties={searchableProperties} /> : 'None'}
                </div>
              </section>
            </div>
          </Tab>
          {children}
        </Tabs>
      </div>
    </div>;
};

<ObjectDefinitionCard
  objectName="Course"
  singularName="Course"
  pluralName="Courses"
  objectId="0-410"
  fullyQualifiedName="COURSE"
  allowsCustomProperties={true}
  allowsSensitiveProperties={false}
  hasPipelines={true}
  description="A structured program or series of lessons, trainings, or educational modules."
  requiredProperties={[
{ label: "Course Name", name: "hs_course_name" }
]}
  primaryDisplayProperty={{ label: "Course Name", name: "hs_course_name" }}
  secondaryDisplayProperties={[
{ label: "Course ID", name: "hs_course_id" }
]}
  searchableProperties={[
{ label: "Course Name", name: "hs_course_name" },
{ label: "Course ID", name: "hs_course_id" }
]}
>
  <Tab title="Properties">
    Below are the available default properties that can be set for each course. To retrieve the complete list of course properties available in your specific account, including any custom properties, make a `GET` request to `/crm/properties/2026-03/courses`. See the [Properties API documentation](/api-reference/latest/crm/properties/guide) for full details.

    <Note>
      Note that your account's available properties may vary based on:

      * Your HubSpot subscription level and enabled features.
      * Custom properties added by your organization.
      * Account age (older accounts may have slightly different property variations).
    </Note>

    <DefaultPropertyListing data={defaultCourseProperties} />
  </Tab>

  <Tab title="Associations">
    <AssociationsTable data={associationsData} objectMetadata={objectMetadata} objectType="course" />
  </Tab>
</ObjectDefinitionCard>
