Object Types API
The Object Types API allows organizations to define custom object types with specific schemas and behaviors.
Register Object Type
Registers a new custom object type for the organization. This enables the creation of specialized objects on boards with custom schemas and behaviors.
- URL:
/object-types - Method:
POST - Headers:
X-API-Key: Organization API key (Required)
- Body:
{
"typeName": "vocabulary-card",
"displayName": "Vocabulary Card",
"category": "educational", // Optional
"schema": {
"type": "object",
"properties": {
"word": { "type": "string", "minLength": 1 },
"translation": { "type": "string", "minLength": 1 },
"pronunciation": { "type": "string", "format": "uri" },
"image_url": { "type": "string", "format": "uri" }
},
"required": ["word", "translation"]
},
"renderConfig": {
"component": "VocabularyCard",
"defaultProps": { "width": 200, "height": 150 },
"previewTemplate": "<div>{{word}} - {{translation}}</div>"
},
"interactions": { // Optional
"draggable": true,
"resizable": true,
"editable": true
}
}Response (201 Created)
{
"id": "uuid",
"name": "vocabulary-card",
"schema": { ... },
"organization_id": "org-uuid",
"created_at": "2025-11-17T10:00:00.000Z"
}Get All Object Types
Retrieves all custom object types registered for the authenticated organization.
- URL:
/object-types - Method:
GET - Headers:
X-API-Key: Organization API key (Required)
Response
[
{
"id": "uuid",
"name": "vocabulary-card",
"schema": { ... },
"organization_id": "org-uuid",
"created_at": "2025-11-17T10:00:00.000Z"
}
]Get Object Type by Name
Retrieves a specific custom object type by its name.
- URL:
/object-types/:typeName - Method:
GET - Headers:
X-API-Key: Organization API key (Required)
Response
{
"id": "uuid",
"name": "vocabulary-card",
"schema": { ... },
"organization_id": "org-uuid",
"created_at": "2025-11-17T10:00:00.000Z"
}When to Use Object Types
Object Types are best for structured data cards that need validation but don't require complex interactivity.
Object Types vs Components SDK
Choose Object Types when:
- You need simple, validated data structures
- Data is primarily displayed, not interactive
- You want server-side rendering templates
- Use case: vocabulary cards, task cards, simple quizzes
Choose Components SDK when:
- You need complex UI and interactions
- State management across participants is required
- You want full control over rendering
- Use case: Planning Poker, collaborative diagrams, games
Comparison Table
| Feature | Object Types | Components SDK |
|---|---|---|
| Rendering | Server template | Custom iframe |
| Interactivity | Read-only display | Full JavaScript control |
| State Sync | customData only | Shared storage (CRDT-like) |
| Validation | JSON Schema | Custom logic |
| Development | Register via API | CLI + local dev server |
| Performance | Lightweight | Heavier (iframe) |
| Use Cases | Data cards, simple objects | Interactive apps, games |
JSON Schema Examples
Example 1: Vocabulary Card
Perfect for language learning platforms.
{
"typeName": "vocabulary-card",
"displayName": "Vocabulary Card",
"category": "educational",
"schema": {
"type": "object",
"properties": {
"word": {
"type": "string",
"minLength": 1,
"maxLength": 100,
"description": "The word to learn"
},
"translation": {
"type": "string",
"minLength": 1,
"maxLength": 100,
"description": "Translation in target language"
},
"pronunciation": {
"type": "string",
"format": "uri",
"description": "URL to audio pronunciation file"
},
"image_url": {
"type": "string",
"format": "uri",
"description": "Visual aid image URL"
},
"example_sentence": {
"type": "string",
"maxLength": 500,
"description": "Example usage in sentence"
},
"difficulty": {
"type": "string",
"enum": ["beginner", "intermediate", "advanced"],
"description": "Difficulty level"
},
"tags": {
"type": "array",
"items": { "type": "string" },
"maxItems": 10,
"description": "Topic tags (e.g., 'food', 'travel')"
}
},
"required": ["word", "translation"]
},
"renderConfig": {
"component": "VocabularyCard",
"defaultProps": {
"width": 300,
"height": 200,
"showPronunciation": true
},
"previewTemplate": "<div class='vocab-card'><h3>{{word}}</h3><p>{{translation}}</p></div>"
},
"interactions": {
"draggable": true,
"resizable": false,
"editable": true
}
}Use Case: EdTech platforms creating vocabulary lessons. Teachers generate hundreds of cards via AI or import from spreadsheets.
Example 2: Task Card (Project Management)
Kanban-style task tracking.
{
"typeName": "task-card",
"displayName": "Task Card",
"category": "productivity",
"schema": {
"type": "object",
"properties": {
"title": {
"type": "string",
"minLength": 1,
"maxLength": 200,
"description": "Task title"
},
"description": {
"type": "string",
"maxLength": 2000,
"description": "Detailed task description"
},
"status": {
"type": "string",
"enum": ["todo", "in_progress", "review", "done"],
"default": "todo"
},
"priority": {
"type": "string",
"enum": ["low", "medium", "high", "critical"],
"default": "medium"
},
"assignee": {
"type": "string",
"description": "Participant ID of assignee"
},
"dueDate": {
"type": "string",
"format": "date-time",
"description": "ISO 8601 deadline"
},
"estimatedHours": {
"type": "number",
"minimum": 0,
"maximum": 999
},
"tags": {
"type": "array",
"items": { "type": "string" },
"maxItems": 5
},
"attachments": {
"type": "array",
"items": {
"type": "object",
"properties": {
"fileId": { "type": "string" },
"filename": { "type": "string" }
}
},
"maxItems": 10
}
},
"required": ["title", "status"]
},
"renderConfig": {
"component": "TaskCard",
"defaultProps": {
"width": 280,
"height": 180,
"showAssignee": true,
"colorByPriority": true
},
"previewTemplate": "<div class='task-card'><strong>{{title}}</strong><span class='status'>{{status}}</span></div>"
}
}Use Case: Agile teams managing sprints. Each column in Kanban is a Frame, cards move between statuses.
Example 3: Quiz Question
Interactive assessment tool.
{
"typeName": "quiz-question",
"displayName": "Quiz Question",
"category": "educational",
"schema": {
"type": "object",
"properties": {
"question": {
"type": "string",
"minLength": 5,
"maxLength": 500,
"description": "The question text"
},
"questionType": {
"type": "string",
"enum": ["multiple_choice", "true_false", "short_answer", "essay"],
"description": "Type of question"
},
"options": {
"type": "array",
"items": {
"type": "object",
"properties": {
"id": { "type": "string" },
"text": { "type": "string" },
"isCorrect": { "type": "boolean" }
},
"required": ["id", "text"]
},
"minItems": 2,
"maxItems": 6,
"description": "Answer options (for multiple choice)"
},
"correctAnswer": {
"type": "string",
"description": "Correct answer (for short answer/essay)"
},
"explanation": {
"type": "string",
"maxLength": 1000,
"description": "Explanation shown after answering"
},
"points": {
"type": "number",
"minimum": 1,
"maximum": 100,
"default": 10,
"description": "Points awarded for correct answer"
},
"difficulty": {
"type": "string",
"enum": ["easy", "medium", "hard"]
},
"topic": {
"type": "string",
"description": "Subject topic"
},
"timeLimit": {
"type": "number",
"minimum": 10,
"maximum": 600,
"description": "Time limit in seconds"
}
},
"required": ["question", "questionType"]
},
"renderConfig": {
"component": "QuizQuestion",
"defaultProps": {
"width": 400,
"height": 300,
"showFeedback": true
}
}
}Use Case: Online exams with instant feedback. Each question is isolated in a Student Zone for academic integrity.
Best Practices for Schema Design
1. Keep Schemas Focused
Each object type should represent one clear concept. Don't create "universal" objects with dozens of optional fields.
Good:
vocabulary-card- language learningtask-card- task managementquiz-question- assessments
Bad:
generic-card- can be anything (too vague)
2. Use Descriptive Property Names
Property names should be self-explanatory and use camelCase.
// Good
{
"firstName": "...",
"emailAddress": "...",
"phoneNumber": "..."
}
// Bad
{
"fn": "...",
"email": "...",
"phone": "..."
}3. Set Reasonable Constraints
Use minLength, maxLength, minimum, maximum to prevent abuse and ensure data quality.
{
"title": {
"type": "string",
"minLength": 1, // Prevent empty strings
"maxLength": 200 // Prevent excessive data
}
}4. Leverage Enums for Fixed Options
Use enum instead of free-form strings when options are limited.
{
"status": {
"type": "string",
"enum": ["draft", "published", "archived"]
}
}This enables:
- Frontend dropdowns
- Validation
- Filtering and search
5. Use Standard Formats
JSON Schema supports formats like uri, email, date-time.
{
"website": {
"type": "string",
"format": "uri"
},
"dueDate": {
"type": "string",
"format": "date-time" // ISO 8601
}
}6. Provide Descriptions
Always include description fields for documentation.
{
"difficulty": {
"type": "string",
"enum": ["beginner", "intermediate", "advanced"],
"description": "Difficulty level for content targeting"
}
}Validation
Server-Side Validation
All object creation and updates are validated against the registered schema:
- Schema Syntax Check - When registering object type
- Data Validation - When creating/updating objects
- Type Enforcement - customData must match schema
Validation Errors
Example Request:
POST /boards/:uuid/objects
{
"type": "vocabulary-card",
"customData": {
"word": "", // Too short (minLength: 1)
"translation": "hello"
}
}Response (400 Bad Request):
{
"statusCode": 400,
"message": "Validation failed",
"errors": [
{
"field": "customData.word",
"constraint": "minLength",
"message": "Property 'word' must have minimum length of 1"
}
]
}Client-Side Validation
Fetch schema before creating objects to validate on frontend:
// Fetch schema
const schema = await fetch('/api/v1/object-types/vocabulary-card', {
headers: { 'X-API-Key': apiKey }
}).then(r => r.json());
// Use schema for form validation
import Ajv from 'ajv';
const ajv = new Ajv();
const validate = ajv.compile(schema.schema);
const isValid = validate({
word: formData.word,
translation: formData.translation
});
if (!isValid) {
console.error(validate.errors);
}Error Handling
Common Errors
409 Conflict - Object Type Already Exists
{
"statusCode": 409,
"message": "Object type \"vocabulary-card\" already exists"
}Solution: Use a different typeName or update existing type (if supported).
400 Bad Request - Invalid Schema
{
"statusCode": 400,
"message": "Invalid JSON Schema",
"errors": [
{
"message": "Property 'type' is required in schema"
}
]
}Solution: Ensure schema follows JSON Schema Draft 7 specification.
402 Payment Required - Quota Exceeded
{
"statusCode": 402,
"message": "Object type quota exceeded",
"current": 10,
"limit": 10,
"upgradeRequired": true
}Solution: Upgrade plan or delete unused object types.
404 Not Found - Object Type Not Found
{
"statusCode": 404,
"message": "Object type \"task-card\" not found"
}Solution: Check typeName spelling or register the type first.
Quota Limits
Object types are subject to plan limits:
| Plan | Max Object Types | Description |
|---|---|---|
| Starter | 3 | Basic types only |
| Professional | 10 | Most common use cases |
| Business | 50 | Multi-product platforms |
| Enterprise | Unlimited | Custom limits |
Check current usage:
GET /quota/usageTips and Tricks
Versioning Schemas
Object type schemas are immutable after creation. To evolve:
- Additive Changes - Add optional properties (backward compatible)
- New Type - Create
vocabulary-card-v2for breaking changes - Migration - Update existing objects to new type via API
Reusing Schemas
Store common schema fragments in your codebase:
// schemas/common.ts
export const personSchema = {
type: "object",
properties: {
name: { type: "string" },
email: { type: "string", format: "email" }
}
};
// Compose in object types
const taskSchema = {
type: "object",
properties: {
title: { type: "string" },
assignee: personSchema // Reuse
}
};Testing Schemas
Before registering in production, test with sample data:
import Ajv from 'ajv';
const schema = { /* your schema */ };
const testData = { /* sample object */ };
const ajv = new Ajv();
const validate = ajv.compile(schema);
const isValid = validate(testData);
console.log(isValid ? 'Valid' : validate.errors);Related Documentation
- Objects in Core Concepts
- Boards API
- Components SDK - For complex interactivity
- JSON Schema Specification