0
# Custom Objects & Dynamic Schemas
1
2
Support for HubSpot custom objects with runtime schema discovery, automatic property mapping, and dynamic stream generation for extensible data models.
3
4
## Capabilities
5
6
### Dynamic Stream Generation
7
8
Custom object streams are generated dynamically based on HubSpot schema definitions discovered at runtime.
9
10
```yaml { .api }
11
custom_object_streams:
12
type: DynamicDeclarativeStream
13
stream_template:
14
type: StateDelegatingStream
15
name: "{{ name }}" # Generated from custom object schema
16
json_schema:
17
$schema: "http://json-schema.org/draft-07/schema#"
18
type: ["null", "object"]
19
additionalProperties: true
20
properties:
21
id:
22
type: ["null", "string"]
23
createdAt:
24
type: ["null", "string"]
25
format: "date-time"
26
updatedAt:
27
type: ["null", "string"]
28
format: "date-time"
29
archived:
30
type: ["null", "boolean"]
31
properties:
32
type: ["null", "object"]
33
# Dynamic properties based on custom object schema
34
35
components_resolver:
36
type: HttpComponentsResolver
37
retriever:
38
path: "/crm/v3/schemas"
39
```
40
41
**Generated Stream Names:**
42
- Custom object schema name becomes stream name
43
- Follows pattern: `{custom_object_name}` (e.g., `vehicles`, `projects`, `invoices`)
44
45
### Custom Object Schema Discovery
46
47
Runtime schema discovery process for custom objects.
48
49
```yaml { .api }
50
schema_discovery_process:
51
1_fetch_schemas:
52
path: "/crm/v3/schemas"
53
description: "Retrieve all custom object schemas"
54
55
2_process_properties:
56
source: "properties array from schema response"
57
transformation: "Convert HubSpot property definitions to JSON schema"
58
59
3_generate_streams:
60
process: "Create stream definition for each custom object"
61
naming: "Use custom object name as stream name"
62
63
4_apply_configuration:
64
incremental_sync: true
65
cursor_field: "updatedAt"
66
primary_key: ["id"]
67
```
68
69
### Custom Object Schema Structure
70
71
```yaml { .api }
72
CustomObjectSchema:
73
type: object
74
properties:
75
id:
76
type: string
77
description: "Custom object schema identifier"
78
name:
79
type: string
80
description: "Custom object name (used as stream name)"
81
labels:
82
type: object
83
properties:
84
singular:
85
type: string
86
description: "Singular label"
87
plural:
88
type: string
89
description: "Plural label"
90
primaryDisplayProperty:
91
type: string
92
description: "Primary display property name"
93
secondaryDisplayProperties:
94
type: array
95
items:
96
type: string
97
description: "Secondary display properties"
98
searchableProperties:
99
type: array
100
items:
101
type: string
102
description: "Properties that can be searched"
103
properties:
104
type: array
105
items:
106
$ref: "#/definitions/CustomObjectProperty"
107
description: "Custom object property definitions"
108
associatedObjects:
109
type: array
110
items:
111
type: string
112
description: "Object types this custom object can associate with"
113
114
CustomObjectProperty:
115
type: object
116
properties:
117
name:
118
type: string
119
description: "Property name"
120
label:
121
type: string
122
description: "Property display label"
123
type:
124
type: string
125
enum: ["string", "number", "boolean", "datetime", "date", "enumeration"]
126
description: "Property data type"
127
fieldType:
128
type: string
129
description: "UI field type"
130
description:
131
type: string
132
description: "Property description"
133
options:
134
type: array
135
items:
136
type: object
137
properties:
138
label:
139
type: string
140
value:
141
type: string
142
description: "Options for enumeration properties"
143
hasUniqueValue:
144
type: boolean
145
description: "Whether property values must be unique"
146
hidden:
147
type: boolean
148
description: "Whether property is hidden in UI"
149
displayOrder:
150
type: integer
151
description: "Property display order"
152
```
153
154
### Schema Loader Implementation
155
156
```yaml { .api }
157
HubspotCustomObjectsSchemaLoader:
158
type: SchemaLoader
159
160
schema_generation_process:
161
1_extract_properties:
162
source: "parameters.schema_properties"
163
description: "Property definitions injected by HttpComponentsResolver"
164
165
2_map_types:
166
string_types: ["string", "enumeration", "phone_number", "object_coordinates", "json"]
167
datetime_types: ["datetime", "date-time"]
168
date_types: ["date"]
169
number_types: ["number"]
170
boolean_types: ["boolean", "bool"]
171
172
3_generate_schema:
173
base_properties:
174
- id: string identifier
175
- createdAt: datetime
176
- updatedAt: datetime
177
- archived: boolean
178
dynamic_properties:
179
- properties: nested object with custom properties
180
- properties_{name}: flattened custom properties
181
182
4_apply_nullability:
183
all_types: ["null", "{actual_type}"]
184
description: "All properties nullable for flexibility"
185
```
186
187
### Property Type Mapping
188
189
```yaml { .api }
190
type_mapping:
191
hubspot_to_json_schema:
192
string:
193
json_type: ["null", "string"]
194
examples: ["text", "textarea", "select"]
195
196
enumeration:
197
json_type: ["null", "string"]
198
description: "Dropdown/select options as string values"
199
200
phone_number:
201
json_type: ["null", "string"]
202
format: "phone number string"
203
204
object_coordinates:
205
json_type: ["null", "string"]
206
description: "Geographic coordinates as string"
207
208
json:
209
json_type: ["null", "string"]
210
description: "JSON data stored as string"
211
212
datetime:
213
json_type: ["null", "string"]
214
format: "date-time"
215
216
date:
217
json_type: ["null", "string"]
218
format: "date"
219
220
number:
221
json_type: ["null", "number"]
222
description: "Numeric values"
223
224
boolean:
225
json_type: ["null", "boolean"]
226
description: "True/false values"
227
228
unknown_types:
229
json_type: ["null", "string"]
230
fallback: "Cast unknown types to string with warning"
231
```
232
233
### Custom Object Record Structure
234
235
```yaml { .api }
236
CustomObjectRecord:
237
type: object
238
properties:
239
# Standard fields (all custom objects)
240
id:
241
type: string
242
description: "Unique custom object record identifier"
243
createdAt:
244
type: string
245
format: date-time
246
description: "Record creation timestamp"
247
updatedAt:
248
type: string
249
format: date-time
250
description: "Last update timestamp"
251
archived:
252
type: boolean
253
description: "Whether record is archived"
254
255
# Dynamic custom properties (nested)
256
properties:
257
type: object
258
additionalProperties: true
259
description: "Custom object properties as nested object"
260
261
# Dynamic custom properties (flattened)
262
# Format: properties_{property_name}
263
# Example: properties_vehicle_make, properties_project_status
264
```
265
266
**Example Custom Object Record:**
267
```json
268
{
269
"id": "12345",
270
"createdAt": "2024-01-15T10:30:00Z",
271
"updatedAt": "2024-01-20T14:45:00Z",
272
"archived": false,
273
"properties": {
274
"vehicle_make": "Toyota",
275
"vehicle_model": "Camry",
276
"year": "2023",
277
"color": "Blue",
278
"price": "25000"
279
},
280
"properties_vehicle_make": "Toyota",
281
"properties_vehicle_model": "Camry",
282
"properties_year": "2023",
283
"properties_color": "Blue",
284
"properties_price": "25000"
285
}
286
```
287
288
### Stream Configuration Templates
289
290
```yaml { .api }
291
custom_object_stream_template:
292
primary_key: ["id"]
293
cursor_field: "updatedAt"
294
sync_mode: incremental
295
296
retriever:
297
url_base: "https://api.hubapi.com"
298
path: "/crm/v3/objects/{{ name }}"
299
http_method: "GET"
300
301
paginator:
302
type: "DefaultPaginator"
303
page_size: 100
304
pagination_strategy:
305
type: "CursorPagination"
306
page_size: 100
307
cursor_value: "{{ response.paging.next.after }}"
308
309
incremental:
310
type: "DatetimeBasedCursor"
311
cursor_field: "updatedAt"
312
datetime_format: "%Y-%m-%dT%H:%M:%S.%fZ"
313
start_datetime:
314
datetime: "{{ config['start_date'] }}"
315
datetime_format: "%Y-%m-%dT%H:%M:%SZ"
316
```
317
318
### Association Support
319
320
Custom objects support associations with standard and other custom objects.
321
322
```yaml { .api }
323
custom_object_associations:
324
supported_associations:
325
- contacts
326
- companies
327
- deals
328
- tickets
329
- other_custom_objects
330
331
association_retrieval:
332
path: "/crm/v4/associations/{{ custom_object_name }}/{{ association_type }}/batch/read"
333
method: "POST"
334
body:
335
inputs: "{{ record_ids }}"
336
337
association_flattening:
338
format: "Array of associated object IDs"
339
field_naming: "{{ association_type }}"
340
example:
341
contacts: ["101", "102"]
342
companies: ["201"]
343
deals: ["301", "302"]
344
```
345
346
### Runtime Stream Registration
347
348
```yaml { .api }
349
dynamic_stream_registration:
350
discovery_phase:
351
1_fetch_schemas: "GET /crm/v3/schemas"
352
2_filter_active: "Only include active custom object schemas"
353
3_generate_streams: "Create stream definition for each schema"
354
355
stream_registration:
356
stream_name: "{{ schema.name }}"
357
stream_class: "DynamicDeclarativeStream"
358
schema_loader: "HubspotCustomObjectsSchemaLoader"
359
360
configuration_injection:
361
schema_properties: "Injected via HttpComponentsResolver"
362
stream_parameters: "Include custom object schema metadata"
363
```
364
365
### Error Handling
366
367
```yaml { .api }
368
error_handling:
369
schema_discovery_failures:
370
- Log warning if schema endpoint unavailable
371
- Continue with standard streams only
372
- Retry schema discovery on subsequent syncs
373
374
property_type_errors:
375
- Cast unknown property types to string
376
- Log warning for unrecognized types
377
- Continue processing with fallback type
378
379
association_failures:
380
- Log warning if associations unavailable
381
- Continue sync without association data
382
- Preserve primary custom object data
383
```
384
385
### Performance Considerations
386
387
```yaml { .api }
388
performance_optimization:
389
schema_caching:
390
- Cache custom object schemas between syncs
391
- Refresh schema cache on stream discovery
392
- Minimize API calls during runtime
393
394
property_chunking:
395
- Handle large custom property lists
396
- Chunk properties to stay within URL limits
397
- Maintain property request consistency
398
399
concurrent_processing:
400
- Process multiple custom object streams in parallel
401
- Respect rate limits across all streams
402
- Balance throughput with API constraints
403
```
404
405
### Usage Examples
406
407
**Custom Object Stream Configuration:**
408
```yaml
409
source:
410
type: airbyte/source-hubspot
411
config:
412
credentials:
413
credentials_title: "Private App Credentials"
414
access_token: "${HUBSPOT_ACCESS_TOKEN}"
415
start_date: "2023-01-01T00:00:00Z"
416
417
# Custom object streams are automatically discovered and available
418
# Stream names match custom object schema names in HubSpot
419
```
420
421
**Querying Custom Object Data:**
422
```sql
423
-- Query custom vehicle object
424
SELECT
425
id,
426
properties_vehicle_make,
427
properties_vehicle_model,
428
properties_year,
429
properties_price,
430
createdAt,
431
updatedAt
432
FROM vehicles
433
WHERE properties_year >= '2020'
434
ORDER BY updatedAt DESC;
435
436
-- Query custom project object with associations
437
SELECT
438
p.id,
439
p.properties_project_name,
440
p.properties_status,
441
p.contacts, -- Associated contact IDs
442
p.companies -- Associated company IDs
443
FROM projects p
444
WHERE p.properties_status = 'active';
445
```