Message Models
Complete documentation of DDEX message structures, headers, and top-level organization.
Overview
DDEX messages are the root containers for all metadata exchanges. They define the communication context, control information, and structure for releases, resources, and commercial terms.
Message Types
Message Type | Purpose | Common Use Cases |
---|---|---|
NewReleaseMessage | Deliver new release metadata | Album/single launches |
ReleaseNotificationMessage | Notify of release changes | Updated metadata |
PurgeReleaseMessage | Remove release from catalog | Content takedowns |
CatalogListMessage | Provide catalog inventory | Bulk catalog delivery |
Graph Model
Message Structure
interface Message {
MessageHeader: MessageHeader;
UpdateIndicator: UpdateIndicator;
MessageControlType?: MessageControlType;
CatalogTransfer?: CatalogTransfer;
WorkList?: Work[];
ReleaseList?: Release[];
ResourceList?: Resource[];
ChapterList?: Chapter[];
CueSheetList?: CueSheet[];
DealList?: Deal[];
Extensions?: ExtensionData[];
}
MessageHeader
interface MessageHeader {
MessageId: MessageId;
MessageFileName?: string;
MessageSender: PartyDescriptor;
SentOnBehalfOf?: PartyDescriptor;
MessageRecipient: PartyDescriptor;
MessageCreatedDateTime: DateTime;
MessageAuditTrail?: MessageAuditTrail;
Comment?: string;
MessageControlType?: MessageControlType;
}
Core Header Fields
Field | Type | Required | Description |
---|---|---|---|
MessageId | MessageId | ✓ | Unique message identifier |
MessageSender | PartyDescriptor | ✓ | Sender organization details |
MessageRecipient | PartyDescriptor | ✓ | Recipient organization details |
MessageCreatedDateTime | DateTime | ✓ | Message creation timestamp |
SentOnBehalfOf | PartyDescriptor | ○ | Third-party sender |
MessageAuditTrail | MessageAuditTrail | ○ | Processing history |
Example Graph Structure
{
MessageHeader: {
MessageId: "MSG_LABEL_20240115_001",
MessageSender: {
PartyId: [
{ Value: "DPID:PADPIDA2014101001U", Namespace: "DPID" }
],
PartyName: [
{ FullName: "Independent Records Ltd" }
],
TradingName: "Indie Records"
},
MessageRecipient: {
PartyId: [
{ Value: "DPID:PADPIDA2014101002U", Namespace: "DPID" }
],
PartyName: [
{ FullName: "Global Streaming Platform Inc" }
]
},
MessageCreatedDateTime: "2024-01-15T10:30:00Z"
},
UpdateIndicator: "OriginalMessage",
MessageControlType: "LiveMessage"
}
Flattened Model
Simplified Message Info
interface FlatMessageInfo {
messageId: string;
messageType: string;
version: string;
sender: {
name: string;
id?: string;
tradingName?: string;
};
recipient: {
name: string;
id?: string;
};
sentOnBehalfOf?: {
name: string;
id?: string;
};
createdDateTime: string;
updateIndicator: string;
messageControlType: string;
comment?: string;
}
Example Flattened Structure
{
messageInfo: {
messageId: "MSG_LABEL_20240115_001",
messageType: "NewReleaseMessage",
version: "4.3",
sender: {
name: "Independent Records Ltd",
id: "DPID:PADPIDA2014101001U",
tradingName: "Indie Records"
},
recipient: {
name: "Global Streaming Platform Inc",
id: "DPID:PADPIDA2014101002U"
},
createdDateTime: "2024-01-15T10:30:00Z",
updateIndicator: "OriginalMessage",
messageControlType: "LiveMessage"
}
}
Message Control Types
Standard Control Types
Control Type | Purpose | Behavior |
---|---|---|
TestMessage | Testing and validation | Not processed in production |
LiveMessage | Production message | Full processing |
SandBoxMessage | Sandbox environment | Limited processing |
Update Indicators
Indicator | Purpose | Effect |
---|---|---|
OriginalMessage | New content | Create new records |
UpdateMessage | Modify existing | Update existing records |
PurgeMessage | Remove content | Delete records |
Party Descriptors
Party Information
interface PartyDescriptor {
PartyId?: PartyId[];
PartyName?: PartyName[];
TradingName?: string;
PartyReference?: PartyReference;
ArtistRole?: ArtistRole[];
DisplayArtist?: DisplayArtist[];
}
Party Identifiers
interface PartyId {
Value: string;
Namespace?: string;
IsDPID?: boolean;
}
// Common party identifier types
type PartyIdNamespace =
| 'DPID' // DDEX Party Identifier
| 'ISNI' // International Standard Name Identifier
| 'IPI' // Interested Parties Information
| 'Proprietary';
Party Names
interface PartyName {
FullName: string;
FullNameAsciiTranscribed?: string;
FullNameIndexed?: string;
NamesBeforeKeyName?: string;
KeyName?: string;
NamesAfterKeyName?: string;
LanguageAndScriptCode?: string;
}
Message Validation
Required Fields by Version
ERN 3.8.2
- MessageId
- MessageSender (with DPID)
- MessageRecipient
- MessageCreatedDateTime
- UpdateIndicator
ERN 4.2/4.3
- All ERN 3.8.2 requirements
- Enhanced party identification
- Optional message audit trail
- Extended control types
Business Rules
Message ID Format
^[A-Z0-9_-]+$
- Must be unique within sender's namespace
- Typically includes sender ID and timestamp
- Maximum length: 50 characters
DateTime Requirements
// ISO 8601 format with timezone
"2024-01-15T10:30:00Z" // UTC
"2024-01-15T10:30:00+01:00" // With timezone offset
Party Validation
- At least one PartyId or PartyName required
- DPID format:
PADPIDA{YYYY}{MM}{DD}{NNN}{C}
- ISNI format: 16 digits with optional formatting
Message Assembly
TypeScript Example
import { MessageBuilder } from 'ddex-builder';
const message = new MessageBuilder()
.setMessageId('MSG_INDIE_20240115_001')
.setSender({
partyId: 'DPID:PADPIDA2014101001U',
partyName: 'Independent Records Ltd',
tradingName: 'Indie Records'
})
.setRecipient({
partyId: 'DPID:PADPIDA2014101002U',
partyName: 'Global Streaming Platform Inc'
})
.setUpdateIndicator('OriginalMessage')
.setMessageControlType('LiveMessage')
.addReleases(releases)
.addResources(resources)
.addDeals(deals)
.build();
Python Example
from ddex_builder import MessageBuilder
message = MessageBuilder() \
.set_message_id('MSG_INDIE_20240115_001') \
.set_sender(
party_id='DPID:PADPIDA2014101001U',
party_name='Independent Records Ltd',
trading_name='Indie Records'
) \
.set_recipient(
party_id='DPID:PADPIDA2014101002U',
party_name='Global Streaming Platform Inc'
) \
.set_update_indicator('OriginalMessage') \
.set_message_control_type('LiveMessage') \
.add_releases(releases) \
.add_resources(resources) \
.add_deals(deals) \
.build()
Message Processing Patterns
Batch Processing
// Process multiple messages
interface MessageBatch {
messages: Message[];
batchId: string;
priority: 'High' | 'Normal' | 'Low';
deliveryDate?: string;
}
class MessageProcessor {
async processBatch(batch: MessageBatch): Promise<ProcessingResult[]> {
return Promise.all(
batch.messages.map(message => this.processMessage(message))
);
}
private async processMessage(message: Message): Promise<ProcessingResult> {
// Validate message structure
const validation = await this.validateMessage(message);
if (!validation.isValid) {
return { success: false, errors: validation.errors };
}
// Process based on update indicator
switch (message.UpdateIndicator) {
case 'OriginalMessage':
return await this.createNewContent(message);
case 'UpdateMessage':
return await this.updateExistingContent(message);
case 'PurgeMessage':
return await this.removeContent(message);
default:
return { success: false, errors: ['Unknown update indicator'] };
}
}
}
Message Routing
interface MessageRoute {
messageType: string;
sender: string;
recipient: string;
processingRules: ProcessingRule[];
}
class MessageRouter {
private routes: Map<string, MessageRoute> = new Map();
registerRoute(route: MessageRoute): void {
const key = `${route.sender}:${route.recipient}:${route.messageType}`;
this.routes.set(key, route);
}
routeMessage(message: Message): ProcessingRule[] {
const sender = this.extractPartyId(message.MessageHeader.MessageSender);
const recipient = this.extractPartyId(message.MessageHeader.MessageRecipient);
const messageType = this.determineMessageType(message);
const key = `${sender}:${recipient}:${messageType}`;
const route = this.routes.get(key);
return route?.processingRules || [];
}
}
Error Handling
Common Message Errors
Error Type | Code | Description | Resolution |
---|---|---|---|
Invalid MessageId | MSG001 | Malformed message identifier | Use valid format |
Unknown Sender | MSG002 | Sender not registered | Register sender party |
Invalid DateTime | MSG003 | Malformed timestamp | Use ISO 8601 format |
Missing Required Field | MSG004 | Required field absent | Add missing field |
Duplicate MessageId | MSG005 | MessageId already used | Use unique identifier |
Error Response Format
interface MessageError {
code: string;
severity: 'Error' | 'Warning' | 'Info';
description: string;
xpath?: string;
context?: Record<string, any>;
}
interface ProcessingResult {
messageId: string;
success: boolean;
errors: MessageError[];
warnings: MessageError[];
processingTime: number;
recordsProcessed: number;
}
Version Differences
ERN 3.8.2 Limitations
- Limited party identification options
- Basic message control types
- Simplified audit trail
ERN 4.2 Enhancements
- Enhanced party descriptors
- Extended message control types
- Improved namespace handling
ERN 4.3 Features
- Full streaming platform support
- Advanced message routing
- Enhanced error reporting
- Improved internationalization
Best Practices
Message Design
- Unique IDs: Ensure MessageIds are globally unique
- Descriptive Names: Use clear party names and trading names
- Appropriate Control: Choose correct message control type
- Audit Trail: Include audit information for tracking
- Extensions: Use extensions sparingly and document well
Performance Optimization
- Batch Messages: Group related releases in single messages
- Lazy Loading: Load message components on demand
- Compression: Use compression for large messages
- Caching: Cache frequently accessed party information
- Validation: Pre-validate before full processing
Security Considerations
- Authentication: Verify sender identity
- Authorization: Validate sender permissions
- Integrity: Check message tampering
- Audit: Log all message processing
- Encryption: Encrypt sensitive party information