Skip to main content

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 TypePurposeCommon Use Cases
NewReleaseMessageDeliver new release metadataAlbum/single launches
ReleaseNotificationMessageNotify of release changesUpdated metadata
PurgeReleaseMessageRemove release from catalogContent takedowns
CatalogListMessageProvide catalog inventoryBulk 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​

FieldTypeRequiredDescription
MessageIdMessageId✓Unique message identifier
MessageSenderPartyDescriptor✓Sender organization details
MessageRecipientPartyDescriptor✓Recipient organization details
MessageCreatedDateTimeDateTime✓Message creation timestamp
SentOnBehalfOfPartyDescriptor○Third-party sender
MessageAuditTrailMessageAuditTrail○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 TypePurposeBehavior
TestMessageTesting and validationNot processed in production
LiveMessageProduction messageFull processing
SandBoxMessageSandbox environmentLimited processing

Update Indicators​

IndicatorPurposeEffect
OriginalMessageNew contentCreate new records
UpdateMessageModify existingUpdate existing records
PurgeMessageRemove contentDelete 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 TypeCodeDescriptionResolution
Invalid MessageIdMSG001Malformed message identifierUse valid format
Unknown SenderMSG002Sender not registeredRegister sender party
Invalid DateTimeMSG003Malformed timestampUse ISO 8601 format
Missing Required FieldMSG004Required field absentAdd missing field
Duplicate MessageIdMSG005MessageId already usedUse 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​

  1. Unique IDs: Ensure MessageIds are globally unique
  2. Descriptive Names: Use clear party names and trading names
  3. Appropriate Control: Choose correct message control type
  4. Audit Trail: Include audit information for tracking
  5. Extensions: Use extensions sparingly and document well

Performance Optimization​

  1. Batch Messages: Group related releases in single messages
  2. Lazy Loading: Load message components on demand
  3. Compression: Use compression for large messages
  4. Caching: Cache frequently accessed party information
  5. Validation: Pre-validate before full processing

Security Considerations​

  1. Authentication: Verify sender identity
  2. Authorization: Validate sender permissions
  3. Integrity: Check message tampering
  4. Audit: Log all message processing
  5. Encryption: Encrypt sensitive party information