Extending Objects
Add or change HotCRM objects, fields, hooks, flows, and sharing metadata.
Extending Objects
HotCRM objects live in src/objects/ and are registered from src/objects/index.ts.
Add an object
// src/objects/warranty.object.ts
import { ObjectSchema, Field } from '@objectstack/spec/data';
export const Warranty = ObjectSchema.create({
name: 'crm_warranty',
label: 'Warranty',
pluralLabel: 'Warranties',
icon: 'shield',
fields: {
warranty_number: Field.autonumber({ label: 'Warranty Number', format: 'WR-{000000}' }),
crm_account: Field.lookup('crm_account', { label: 'Account', required: true }),
crm_product: Field.lookup('crm_product', { label: 'Product' }),
status: Field.select({
label: 'Status',
defaultValue: 'active',
options: [
{ label: 'Active', value: 'active', default: true },
{ label: 'Expired', value: 'expired' },
],
}),
},
enable: {
apiEnabled: true,
searchable: true,
trackHistory: true,
},
});Then export it:
// src/objects/index.ts
export { Warranty } from './warranty.object.js';Add lifecycle logic
Hooks live beside objects and are collected by src/hooks/index.ts.
// src/objects/warranty.hook.ts
import type { Hook } from '@objectstack/spec/data';
const warrantyHook: Hook = {
name: 'crm_warranty_hook',
object: 'crm_warranty',
events: ['beforeInsert', 'beforeUpdate'],
handler: async (ctx) => {
const doc = ctx.input.doc as Record<string, unknown>;
if (doc.status === 'expired' && !doc.end_date) {
throw new Error('Expired warranties need an end date.');
}
},
};
export default warrantyHook;Add automation
Use src/flows/ for multi-step automation and export new flows from src/flows/index.ts.
import type * as Automation from '@objectstack/spec/automation';
type Flow = Automation.Flow;
export const WarrantyAlertFlow: Flow = {
name: 'warranty_alert',
label: 'Warranty Alert',
type: 'record_change',
status: 'active',
variables: [],
nodes: [
{ id: 'start', type: 'start', label: 'Warranty updated', config: { objectName: 'crm_warranty', triggerType: 'record-after-update' } },
{ id: 'end', type: 'end', label: 'End' },
],
edges: [{ id: 'e1', source: 'start', target: 'end', type: 'default' }],
};Verify
pnpm validate
pnpm typecheck
pnpm test