0
# Data Models and Types
1
2
Rich Pydantic models covering all Linear entities with complete field definitions, input/output types, enums, and connection types for GraphQL pagination.
3
4
## Core Data Models
5
6
### Issue Models
7
8
Comprehensive models for issue management with full field coverage and dynamic properties.
9
10
```python { .api }
11
class LinearIssue(LinearModel):
12
# Required fields
13
id: str
14
title: str
15
url: str
16
state: LinearState
17
priority: LinearPriority
18
team: LinearTeam
19
createdAt: datetime
20
updatedAt: datetime
21
number: int
22
customerTicketCount: int
23
24
# Optional fields (50+ additional fields)
25
description: Optional[str] = None
26
assignee: Optional[LinearUser] = None
27
project: Optional[LinearProject] = None
28
labels: Optional[List[LinearLabel]] = None
29
dueDate: Optional[datetime] = None
30
parentId: Optional[str] = None
31
estimate: Optional[int] = None
32
attachments: Optional[List[LinearAttachment]] = None
33
# ... many more optional fields
34
35
# Dynamic properties (accessed through client reference)
36
@property
37
def parent(self) -> Optional['LinearIssue']: ...
38
39
@property
40
def children(self) -> Dict[str, 'LinearIssue']: ...
41
42
@property
43
def comments(self) -> List['Comment']: ...
44
45
@property
46
def history(self) -> List[Dict[str, Any]]: ...
47
48
@property
49
def relations(self) -> List['IssueRelation']: ...
50
51
@property
52
def metadata(self) -> Dict[str, Any]: ...
53
```
54
55
```python { .api }
56
class LinearIssueInput(LinearModel):
57
"""Input model for creating issues with metadata auto-conversion."""
58
title: str # Required
59
teamName: str # Required
60
description: Optional[str] = None
61
priority: Optional[LinearPriority] = None
62
assigneeId: Optional[str] = None
63
projectId: Optional[str] = None
64
stateId: Optional[str] = None
65
parentId: Optional[str] = None
66
labelIds: Optional[List[str]] = None
67
estimate: Optional[int] = None
68
dueDate: Optional[datetime] = None
69
metadata: Optional[Dict[str, Any]] = None # Auto-converted to attachment
70
```
71
72
```python { .api }
73
class LinearIssueUpdateInput(LinearModel):
74
"""Input model for updating issues with partial field updates."""
75
title: Optional[str] = None
76
description: Optional[str] = None
77
priority: Optional[LinearPriority] = None
78
assigneeId: Optional[str] = None
79
projectId: Optional[str] = None
80
stateId: Optional[str] = None
81
teamId: Optional[str] = None
82
labelIds: Optional[List[str]] = None
83
estimate: Optional[int] = None
84
dueDate: Optional[datetime] = None
85
metadata: Optional[Dict[str, Any]] = None
86
```
87
88
Usage examples:
89
90
```python
91
from linear_api import LinearIssue, LinearIssueInput, LinearPriority
92
93
# Create issue input
94
issue_input = LinearIssueInput(
95
title="New feature request",
96
teamName="Engineering",
97
description="Detailed feature description",
98
priority=LinearPriority.HIGH,
99
metadata={"category": "feature", "source": "customer-feedback"}
100
)
101
102
# Access issue properties
103
issue = client.issues.get("issue-id")
104
print(f"Issue: {issue.title}")
105
print(f"State: {issue.state.name}")
106
print(f"Priority: {issue.priority}")
107
print(f"Assignee: {issue.assignee.name if issue.assignee else 'Unassigned'}")
108
109
# Access dynamic properties
110
children = issue.children # Automatically fetched from API
111
comments = issue.comments # Automatically fetched with pagination
112
```
113
114
### Label and Attachment Models
115
116
Models for issue categorization and file attachments.
117
118
```python { .api }
119
class LinearLabel(LinearModel):
120
id: str
121
name: str
122
color: str
123
archivedAt: Optional[datetime] = None
124
createdAt: Optional[datetime] = None
125
updatedAt: Optional[datetime] = None
126
description: Optional[str] = None
127
isGroup: Optional[bool] = None
128
inheritedFrom: Optional[Dict[str, Any]] = None
129
parent: Optional[Dict[str, Any]] = None
130
creator: Optional[LinearUser] = None
131
issue_ids: Optional[List[str]] = None # When include_issue_ids=True
132
```
133
134
```python { .api }
135
class LinearAttachment(LinearModel):
136
id: str
137
url: str
138
title: Optional[str] = None
139
subtitle: Optional[str] = None
140
metadata: Optional[Dict[str, Any]] = None
141
issueId: str
142
createdAt: datetime
143
updatedAt: datetime
144
creator: Optional[LinearUser] = None
145
```
146
147
```python { .api }
148
class LinearAttachmentInput(LinearModel):
149
"""Input for creating attachments with metadata support."""
150
url: str # Required
151
issueId: str # Required
152
title: Optional[str] = None
153
subtitle: Optional[str] = None
154
metadata: Optional[Dict[str, Any]] = None
155
```
156
157
Usage examples:
158
159
```python
160
# Create attachment
161
attachment_input = LinearAttachmentInput(
162
url="https://docs.company.com/spec.pdf",
163
issueId="issue-id",
164
title="Technical Specification",
165
subtitle="Version 2.0",
166
metadata={"version": "2.0", "type": "specification"}
167
)
168
169
# Access label information
170
for label in issue.labels or []:
171
print(f"Label: {label.name} ({label.color})")
172
```
173
174
### Project Models
175
176
Comprehensive project management models with lifecycle tracking.
177
178
```python { .api }
179
class LinearProject(LinearModel):
180
# Required fields
181
id: str
182
name: str
183
createdAt: datetime
184
updatedAt: datetime
185
slugId: str
186
url: str
187
color: str
188
priority: int
189
status: ProjectStatus
190
progress: float
191
scope: int
192
193
# Optional fields (20+ additional fields)
194
description: Optional[str] = None
195
startDate: Optional[TimelessDate] = None
196
targetDate: Optional[TimelessDate] = None
197
completedAt: Optional[datetime] = None
198
canceledAt: Optional[datetime] = None
199
health: Optional[str] = None
200
# ... many more optional fields
201
202
# Dynamic properties
203
@property
204
def members(self) -> List['LinearUser']: ...
205
206
@property
207
def issues(self) -> List['LinearIssue']: ...
208
209
@property
210
def projectUpdates(self) -> List['ProjectUpdate']: ...
211
212
@property
213
def relations(self) -> List['ProjectRelation']: ...
214
```
215
216
```python { .api }
217
class ProjectStatus(LinearModel):
218
type: ProjectStatusType # Enum: PLANNED, BACKLOG, STARTED, etc.
219
```
220
221
```python { .api }
222
class ProjectMilestone(LinearModel):
223
id: str
224
name: str
225
# Additional milestone fields...
226
```
227
228
Usage examples:
229
230
```python
231
from linear_api import ProjectStatusType
232
233
# Access project information
234
project = client.projects.get("project-id")
235
print(f"Project: {project.name}")
236
print(f"Status: {project.status.type}")
237
print(f"Progress: {project.progress}%")
238
print(f"Priority: {project.priority}")
239
240
# Check project dates
241
if project.startDate:
242
print(f"Start: {project.startDate.year}-{project.startDate.month}-{project.startDate.day}")
243
if project.targetDate:
244
print(f"Target: {project.targetDate.year}-{project.targetDate.month}-{project.targetDate.day}")
245
```
246
247
### Team Models
248
249
Team configuration and workflow models with extensive settings.
250
251
```python { .api }
252
class LinearTeam(LinearModel):
253
# Required fields
254
id: str
255
name: str
256
key: str
257
258
# Optional configuration fields (50+ fields)
259
description: Optional[str] = None
260
color: Optional[str] = None
261
icon: Optional[str] = None
262
createdAt: Optional[datetime] = None
263
updatedAt: Optional[datetime] = None
264
parentId: Optional[str] = None
265
266
# Automation settings
267
autoArchivePeriod: Optional[int] = None
268
autoCloseChildIssues: Optional[bool] = None
269
autoCloseParentIssues: Optional[bool] = None
270
271
# Cycle configuration
272
cycleDuration: Optional[int] = None
273
cycleStartDay: Optional[int] = None
274
cyclesEnabled: Optional[bool] = None
275
276
# Estimation settings
277
defaultIssueEstimate: Optional[int] = None
278
issueEstimationType: Optional[str] = None
279
280
# Template settings
281
defaultIssueState: Optional[Dict[str, Any]] = None
282
defaultProjectTemplate: Optional[Dict[str, Any]] = None
283
284
# SCIM integration
285
scimGroupName: Optional[str] = None
286
scimManaged: Optional[bool] = None
287
288
# Dynamic properties
289
@property
290
def members(self) -> List['LinearUser']: ...
291
292
@property
293
def states(self) -> List['LinearState']: ...
294
295
@property
296
def labels(self) -> List['LinearLabel']: ...
297
298
@property
299
def activeCycle(self) -> Optional[Dict[str, Any]]: ...
300
301
@property
302
def projects(self) -> Dict[str, Any]: ...
303
```
304
305
```python { .api }
306
class LinearState(LinearModel):
307
id: str
308
name: str
309
type: str
310
color: str
311
archivedAt: Optional[datetime] = None
312
createdAt: Optional[datetime] = None
313
updatedAt: Optional[datetime] = None
314
description: Optional[str] = None
315
position: Optional[int] = None
316
inheritedFrom: Optional[Dict[str, Any]] = None
317
issue_ids: Optional[List[str]] = None # When include_issue_ids=True
318
```
319
320
Usage examples:
321
322
```python
323
# Access team configuration
324
team = client.teams.get("team-id")
325
print(f"Team: {team.name} ({team.key})")
326
print(f"Cycles enabled: {team.cyclesEnabled}")
327
print(f"Cycle duration: {team.cycleDuration}")
328
print(f"Auto-archive period: {team.autoArchivePeriod}")
329
330
# Access team states
331
states = team.states
332
for state in states:
333
print(f"State: {state.name} ({state.type}) - {state.color}")
334
```
335
336
### User Models
337
338
User profile and organizational relationship models.
339
340
```python { .api }
341
class LinearUser(LinearModel):
342
# Required fields
343
id: str
344
name: str
345
displayName: str
346
email: str
347
createdAt: datetime
348
updatedAt: datetime
349
350
# Boolean status fields
351
active: bool
352
admin: bool
353
app: bool
354
guest: bool
355
isMe: bool
356
357
# Optional profile fields
358
avatarUrl: Optional[str] = None
359
archivedAt: Optional[datetime] = None
360
avatarBackgroundColor: Optional[str] = None
361
calendarHash: Optional[str] = None
362
createdIssueCount: Optional[int] = None
363
description: Optional[str] = None
364
disableReason: Optional[str] = None
365
initials: Optional[str] = None
366
inviteHash: Optional[str] = None
367
lastSeen: Optional[datetime] = None
368
statusEmoji: Optional[str] = None
369
statusLabel: Optional[str] = None
370
statusUntilAt: Optional[datetime] = None
371
timezone: Optional[str] = None
372
url: Optional[str] = None
373
374
# Organizational context
375
organization: Optional[Organization] = None
376
377
# Dynamic properties
378
@property
379
def assignedIssues(self) -> Dict[str, 'LinearIssue']: ...
380
381
@property
382
def createdIssues(self) -> List[Dict[str, Any]]: ...
383
384
@property
385
def teams(self) -> List['LinearTeam']: ...
386
387
@property
388
def teamMemberships(self) -> List[Dict[str, Any]]: ...
389
```
390
391
```python { .api }
392
class LinearUserReference(LinearModel):
393
"""Simplified user reference for nested objects."""
394
id: str
395
name: str
396
displayName: str
397
email: Optional[str] = None
398
createdAt: Optional[datetime] = None
399
updatedAt: Optional[datetime] = None
400
avatarUrl: Optional[str] = None
401
```
402
403
Usage examples:
404
405
```python
406
# Access user information
407
user = client.users.get_me()
408
print(f"User: {user.displayName} ({user.email})")
409
print(f"Status: {'Active' if user.active else 'Inactive'}")
410
print(f"Admin: {'Yes' if user.admin else 'No'}")
411
412
# Access profile details
413
if user.statusEmoji:
414
print(f"Status: {user.statusEmoji} {user.statusLabel}")
415
if user.timezone:
416
print(f"Timezone: {user.timezone}")
417
```
418
419
## Enums and Constants
420
421
### Priority and Status Enums
422
423
```python { .api }
424
class LinearPriority(Enum):
425
"""Issue priority levels."""
426
URGENT = 0
427
HIGH = 1
428
MEDIUM = 2
429
LOW = 3
430
NONE = 4
431
```
432
433
```python { .api }
434
class ProjectStatusType(StrEnum):
435
"""Project lifecycle status types."""
436
PLANNED = "planned"
437
BACKLOG = "backlog"
438
STARTED = "started"
439
PAUSED = "paused"
440
COMPLETED = "completed"
441
CANCELED = "canceled"
442
```
443
444
```python { .api }
445
class ProjectUpdateHealthType(StrEnum):
446
"""Project health indicators."""
447
ON_TRACK = "onTrack"
448
AT_RISK = "atRisk"
449
OFF_TRACK = "offTrack"
450
```
451
452
453
### Service Integration Enums
454
455
```python { .api }
456
class IntegrationService(StrEnum):
457
"""Supported integration services."""
458
ASANA = "asana"
459
FIGMA = "figma"
460
GITHUB = "github"
461
GITLAB = "gitlab"
462
INTERCOM = "intercom"
463
JIRA = "jira"
464
NOTION = "notion"
465
SLACK = "slack"
466
ZENDESK = "zendesk"
467
```
468
469
```python { .api }
470
class SLADayCountType(StrEnum):
471
"""SLA day counting methods."""
472
ALL = "all"
473
ONLY_BUSINESS_DAYS = "onlyBusinessDays"
474
```
475
476
Usage examples:
477
478
```python
479
from linear_api import LinearPriority, ProjectStatusType, IntegrationService, SLADayCountType
480
481
# Use priority enum
482
issue_input = LinearIssueInput(
483
title="High priority issue",
484
teamName="Engineering",
485
priority=LinearPriority.HIGH
486
)
487
488
# Use project status enum
489
client.projects.update("project-id", status=ProjectStatusType.STARTED)
490
491
# Use SLA day counting
492
print("SLA counting methods:")
493
for method in SLADayCountType:
494
print(f" - {method.value}")
495
496
# Check integration services
497
print("Supported integrations:")
498
for service in IntegrationService:
499
print(f" - {service.value}")
500
```
501
502
## Base Classes and Utilities
503
504
### Base Model Class
505
506
```python { .api }
507
class LinearModel(BaseModel):
508
"""Base class for all Linear domain models with Pydantic integration."""
509
linear_class_name: ClassVar[str] # Maps to GraphQL type names
510
known_missing_fields: ClassVar[List[str]] = [] # Tracks missing API fields
511
known_extra_fields: ClassVar[List[str]] = [] # Tracks additional fields
512
_client: Any = PrivateAttr() # Private client reference
513
514
def with_client(self, client) -> 'LinearModel':
515
"""Sets client reference for dynamic property access."""
516
517
def model_dump(self, **kwargs) -> Dict[str, Any]:
518
"""Enhanced serialization excluding class variables."""
519
520
@classmethod
521
def get_linear_class_name(cls) -> str:
522
"""Returns GraphQL type name for this model."""
523
```
524
525
### Connection Types
526
527
Generic connection models for GraphQL pagination.
528
529
```python { .api }
530
class Connection(LinearModel, Generic[T]):
531
"""Generic connection model for GraphQL pagination."""
532
nodes: List[T]
533
pageInfo: Optional[Dict[str, Any]] = None
534
```
535
536
Specific connection types for different entities:
537
538
```python { .api }
539
class CommentConnection(LinearModel):
540
nodes: List[Comment]
541
pageInfo: Optional[Dict[str, Any]] = None
542
543
class UserConnection(LinearModel):
544
nodes: List[LinearUserReference]
545
pageInfo: Optional[Dict[str, Any]] = None
546
547
class TeamConnection(LinearModel):
548
nodes: List[LinearTeam]
549
pageInfo: Optional[Dict[str, Any]] = None
550
551
# ... many more connection types for different entities
552
```
553
554
## Common Models
555
556
Shared models used across different domain areas.
557
558
```python { .api }
559
class Organization(LinearModel):
560
id: str
561
name: str
562
563
class Comment(LinearModel):
564
id: str
565
body: str
566
createdAt: datetime
567
updatedAt: datetime
568
creator: Optional[LinearUserReference] = None
569
570
class Document(LinearModel):
571
id: str
572
title: Optional[str] = None
573
icon: Optional[str] = None
574
createdAt: datetime
575
updatedAt: datetime
576
577
class EntityExternalLink(LinearModel):
578
id: str
579
url: str
580
label: Optional[str] = None
581
createdAt: datetime
582
583
class Reaction(LinearModel):
584
id: str
585
emoji: str
586
createdAt: datetime
587
user: Optional[LinearUserReference] = None
588
589
class TimelessDate(LinearModel):
590
"""Date without time information."""
591
year: int
592
month: int
593
day: int
594
```
595
596
## Relationship Models
597
598
Models for managing relationships between entities.
599
600
```python { .api }
601
class IssueRelation(LinearModel):
602
id: str
603
type: str
604
relatedIssue: Optional[Dict[str, Any]] = None
605
createdAt: datetime
606
607
class ProjectRelation(LinearModel):
608
id: str
609
createdAt: datetime
610
type: str
611
project: Optional[Dict[str, Any]] = None
612
relatedProject: Optional[Dict[str, Any]] = None
613
614
class TeamMembership(LinearModel):
615
id: str
616
createdAt: Optional[datetime] = None
617
updatedAt: Optional[datetime] = None
618
archivedAt: Optional[datetime] = None
619
owner: Optional[bool] = None
620
sortOrder: Optional[int] = None
621
user: Optional[LinearUser] = None
622
team: Optional[LinearTeam] = None
623
```
624
625
## Advanced Model Usage
626
627
### Model Validation and Type Safety
628
629
```python
630
from pydantic import ValidationError
631
632
try:
633
# Pydantic validation ensures type safety
634
issue_input = LinearIssueInput(
635
title="Valid issue",
636
teamName="Engineering",
637
priority="INVALID_PRIORITY" # This will raise ValidationError
638
)
639
except ValidationError as e:
640
print(f"Validation error: {e}")
641
642
# Proper usage with enum
643
issue_input = LinearIssueInput(
644
title="Valid issue",
645
teamName="Engineering",
646
priority=LinearPriority.HIGH # Type-safe enum usage
647
)
648
```
649
650
### Dynamic Property Access
651
652
```python
653
# Models with client references can access related data dynamically
654
issue = client.issues.get("issue-id")
655
656
# These properties trigger API calls automatically
657
parent_issue = issue.parent # Fetches parent issue if exists
658
child_issues = issue.children # Fetches all child issues
659
comments = issue.comments # Fetches all comments with pagination
660
history = issue.history # Fetches change history
661
662
# Properties return appropriate model types
663
for comment in comments:
664
print(f"Comment by {comment.creator.name}: {comment.body}")
665
```
666
667
### Model Serialization
668
669
```python
670
# Models can be serialized to dictionaries
671
issue_dict = issue.model_dump()
672
print(f"Issue as dict: {issue_dict}")
673
674
# Or to JSON
675
issue_json = issue.model_dump_json()
676
print(f"Issue as JSON: {issue_json}")
677
678
# Exclude private fields
679
clean_dict = issue.model_dump(exclude={'_client'})
680
```
681
682
### Custom Model Extensions
683
684
```python
685
# Models can be extended with custom properties
686
class ExtendedLinearIssue(LinearIssue):
687
@property
688
def is_overdue(self) -> bool:
689
"""Check if issue is overdue."""
690
if not self.dueDate:
691
return False
692
return datetime.now() > self.dueDate
693
694
@property
695
def priority_name(self) -> str:
696
"""Get human-readable priority name."""
697
priority_names = {
698
LinearPriority.URGENT: "π΄ Urgent",
699
LinearPriority.HIGH: "π High",
700
LinearPriority.MEDIUM: "π‘ Medium",
701
LinearPriority.LOW: "π΅ Low",
702
LinearPriority.NONE: "βͺ None"
703
}
704
return priority_names.get(self.priority, "Unknown")
705
706
# Use extended model
707
issue = client.issues.get("issue-id")
708
extended_issue = ExtendedLinearIssue(**issue.model_dump())
709
print(f"Priority: {extended_issue.priority_name}")
710
print(f"Overdue: {extended_issue.is_overdue}")
711
```