From [MS-PST] sections 2.4 and 2.5 (the learn.microsoft.com PDF, read 2026-11-13). The structures these tables sit in are on the
heaps, tables and messaging page. Property types are written as in [MS-OXPROPS]; String is Unicode, and a
Unicode PST stores string properties as UTF-16 while an ANSI PST stores them as 8-bit text; a reader must be able to return either form.
Properties every object must have
| Object | Required properties |
|---|---|
| Message store PC | 0FF9 PidTagRecordKey (Binary: the provider UID of this PST), 3001 PidTagDisplayName (String), 35E0 PidTagIpmSubTreeEntryId (Binary: EntryID of the root mailbox folder), 35E3 PidTagIpmWastebasketEntryId (Binary: Deleted Items), 35E7 PidTagFinderEntryId (Binary: the search folder). |
| Folder PC (node type normal folder) | 3001 PidTagDisplayName (String), 3602 PidTagContentCount (Integer32), 3603 PidTagContentUnreadCount (Integer32), 360A PidTagSubfolders (Boolean). The node's nidParent is the parent folder, so parents can be found from the folder PC alone. |
| Message PC | 001A PidTagMessageClass (String), 0E07 PidTagMessageFlags, 0E08 PidTagMessageSize, 0E17 PidTagMessageStatus (Integer32), 3007 PidTagCreationTime, 3008 PidTagLastModificationTime (Time), 300B PidTagSearchKey (Binary). The schema of a general message is otherwise in [MS-OXCMSG], [MS-OXOMSG] and [MS-OXPROPS]. The message node's nidParent is the folder it is in, so moving a message to another folder changes only that field (plus the two content tables). |
| Attachment PC (subnode, type attachment) | 0E20 PidTagAttachSize, 3705 PidTagAttachMethod, 370B PidTagRenderingPosition (Integer32). The data is in PidTagAttachDataBinary; for an attached message it is in PidTagAttachDataObject, a PtypObject whose nid is a subnode holding a complete message PC (not in the NBT and with no parent folder). |
The attachment table exists only if the message has at least one attachment (mfHasAttach in PidTagMessageFlags); the number of attachment subnodes must equal its rows. A recipient table must exist for every message.
Both live in the message node's subnode tree (found by NID_ATTACHMENT_TABLE and the recipient table NID).
Template tables
A template table is a table context with no rows and a fixed set of columns. Each PST has one for the folder hierarchy, contents and FAI tables, the search contents table, the recipient table and the attachment table. When a folder is created its three tables are duplicated from the templates, which gives their default columns; the Copied column says whether the property is copied from the child object into the table row when the child is created. Newer Outlook versions may add columns; the lists are the required ones. A parent's table rows duplicate properties of the child objects, so a change to a child must be reflected in the parent's table.
The RowIndex of these tables maps each row to its object: dwRowID is the NID of the sub-folder, message or attachment subnode and dwRowIndex its 0-based row in the row matrix.
Hierarchy table template (NID 0x60D)
| ID | Property | Type | Copied |
|---|---|---|---|
| 0E30 | PidTagReplItemid | Integer32 | no |
| 0E33 | PidTagReplChangenum | Integer64 | no |
| 0E34 | PidTagReplVersionHistory | Binary | no |
| 0E38 | PidTagReplFlags | Integer32 | yes |
| 3001 | PidTagDisplayName (sub-folder name; alternate name PidTagDisplayName_W) | String | yes |
| 3602 | PidTagContentCount | Integer32 | yes |
| 3603 | PidTagContentUnreadCount | Integer32 | yes |
| 360A | PidTagSubfolders | Boolean | yes |
| 3613 | PidTagContainerClass (alternate name PidTagContainerClass_W) | Binary as listed | yes |
| 6635 | PidTagPstHiddenCount (hidden items in the sub-folder) | Integer32 | yes |
| 6636 | PidTagPstHiddenUnread | Integer32 | yes |
| 67F2 | PidTagLtpRowId | Integer32 | yes |
| 67F3 | PidTagLtpRowVer | Integer32 | yes |
Contents table template (NID 0x60E)
| ID | Property | Type | Copied |
|---|---|---|---|
| 0017 | PidTagImportance | Integer32 | yes |
| 001A | PidTagMessageClassW (PidTagMessageClass_W) | String | yes |
| 0036 | PidTagSensitivity | Integer32 | yes |
| 0037 | PidTagSubjectW (PidTagSubject_W) | String | yes |
| 0039 | PidTagClientSubmitTime | Time | yes |
| 0042 | PidTagSentRepresentingNameW | String | yes |
| 0057 | PidTagMessageToMe (recipient is in the To line) | Boolean | yes |
| 0058 | PidTagMessageCcMe | Boolean | yes |
| 0070 | PidTagConversationTopicW | String | yes |
| 0071 | PidTagConversationIndex | Binary | yes |
| 0E03 | PidTagDisplayCcW | String | yes |
| 0E04 | PidTagDisplayToW | String | yes |
| 0E06 | PidTagMessageDeliveryTime | Time | yes |
| 0E07 | PidTagMessageFlags | Integer32 | yes |
| 0E08 | PidTagMessageSize | Integer32 | yes |
| 0E17 | PidTagMessageStatus | Integer32 | yes |
| 0E30 | PidTagReplItemid | Integer32 | yes |
| 0E33 | PidTagReplChangenum | Integer64 | yes |
| 0E34 | PidTagReplVersionHistory | Binary | yes |
| 0E38 | PidTagReplFlags | Integer32 | yes |
| 0E3C | PidTagReplCopiedfromVersionhistory | Binary | yes |
| 0E3D | PidTagReplCopiedfromItemid | Binary | yes |
| 1097 | PidTagItemTemporaryFlags | Integer32 | yes |
| 3008 | PidTagLastModificationTime | Time | yes |
| 65C6 | PidTagSecureSubmitFlags | Integer32 | yes |
| 67F2 | PidTagLtpRowId | Integer32 | yes |
| 67F3 | PidTagLtpRowVer | Integer32 | yes |
FAI contents table template (NID 0x60F)
| ID | Property | Type | Copied |
|---|---|---|---|
| 001A | PidTagMessageClass (PidTagMessageClass_W) | String | yes |
| 0E07 | PidTagMessageFlags | Integer32 | yes |
| 0E17 | PidTagMessageStatus | Integer32 | yes |
| 3001 | PidTagDisplayName (PidTagDisplayName_W) | String | yes |
| 67F2 | PidTagLtpRowId | Integer32 | yes |
| 67F3 | PidTagLtpRowVer | Integer32 | yes |
| 6800 | PidTagOfflineAddressBookName | String | yes |
| 6803 | PidTagSendOutlookRecallReport | Boolean | yes |
| 6805 | PidTagOfflineAddressBookTruncatedProperties | MultipleInteger32 | yes |
| 7003 | PidTagViewDescriptorFlags | Integer32 | yes |
| 7004 | PidTagViewDescriptorLinkTo | Binary | yes |
| 7005 | PidTagViewDescriptorViewFolder | Binary | yes |
| 7006 | PidTagViewDescriptorName | String | yes |
| 7007 | PidTagViewDescriptorVersion | Integer32 | yes |
Search folder contents table template (NID 0x610)
| ID | Property | Type |
|---|---|---|
| 0017 | PidTagImportance | Integer32 |
| 001A | PidTagMessageClassW | String |
| 0036 | PidTagSensitivity | Integer32 |
| 0037 | PidTagSubjectW | String |
| 0042 | PidTagSentRepresentingNameW | String |
| 0057 | PidTagMessageToMe | Boolean |
| 0E03 | PidTagDisplayCcW | String |
| 0E04 | PidTagDisplayToW | String |
| 0E05 | PidTagParentDisplayW | String |
| 0E06 | PidTagMessageDeliveryTime | Time |
| 0E07 | PidTagMessageFlags | Integer32 |
| 0E08 | PidTagMessageSize | Integer32 |
| 0E17 | PidTagMessageStatus | Integer32 |
| 0E2A | PidTagExchangeRemoteHeader | Boolean |
| 3008 | PidTagLastModificationTime | Time |
| 67F1 | PidTagLtpParentNid | Integer32 |
| 67F2 | PidTagLtpRowId | Integer32 |
| 67F3 | PidTagLtpRowVer | Integer32 |
(0E07 PidTagMessageFlags and 0E17 PidTagMessageStatus appear twice in the specification's list; they are one column each.) The search update queue of a search folder (node type 0x06) is changed only through the search management queue and a reader must not modify it; the search criteria object (node type 0x07, a PC) must not be modified, and its properties are out of the scope of [MS-PST].
Recipient table template (NID 0x692)
| ID | Property | Type |
|---|---|---|
| 0C15 | PidTagRecipientType | Integer32 |
| 0E0F | PidTagResponsibility | Boolean |
| 0FF9 | PidTagRecordKey | Binary |
| 0FFE | PidTagObjectType | Integer32 |
| 0FFF | PidTagEntryId | Binary |
| 3001 | PidTagDisplayName (PidTagDisplayName_W) | String |
| 3002 | PidTagAddressType | String |
| 3003 | PidTagEmailAddress | String |
| 300B | PidTagSearchKey | Binary |
| 3900 | PidTagDisplayType | Integer32 |
| 39FF | PidTag7BitDisplayName | String |
| 3A40 | PidTagSendRichInfo | Boolean |
| 67F2 | PidTagLtpRowId | Integer32 |
| 67F3 | PidTagLtpRowVer | Integer32 |
A message's own recipient table has all these columns and more.
Attachment table template (NID 0x671)
| ID | Property | Type |
|---|---|---|
| 0E20 | PidTagAttachSize | Integer32 |
| 3704 | PidTagAttachFilenameW | String |
| 3705 | PidTagAttachMethod | Integer32 |
| 370B | PidTagRenderingPosition | Integer32 |
| 67F2 | PidTagLtpRowId | Integer32 |
| 67F3 | PidTagLtpRowVer | Integer32 |
Calculated properties
Properties a reader presents but the file does not store as such. Each row has a property tag, a base tag (the stored property it is derived from, nid or nidParent meaning that node field), and Get, Set, Delete and List behaviours. A reader must follow them.
PC means the value is read from or written to the property context unchanged; N means not allowed.
List behaviour (when all properties are enumerated)
ALWAYS: included.ALLOW: included only if the PC already has the property.BASED: included only if the base-tag property exists.BODY: included if any of PidTagRtfCompressed, PidTagBody, PidTagBodyHtml or PidTagHtml exists.NEVER: not included, but can still be got and, by the Set and Delete rules, changed.
Get behaviours
| Code | Value |
|---|---|
| SPGET_EID | the PST EntryID of the object, converted from its NID (see the mapping between EntryID and NID in the messaging-layer description) |
| SPGET_STOREEID, SPGET_UIDRESOURCE | the PidTagEntryId of the message store PC |
| SPGET_LONGNID, SPGET_RECORDKEY | the node's NID |
| SPGET_UIDPROVIDER, SPGET_MAPSIG | the provider UID of the PST, stored in the message store's record key |
| SPGET_TRUE, SPGET_ZERO | true; zero (also SPGET_SECURE_SUBMIT_FLAGS and SPGET_CONTENT_FILTER_SCL return zero) |
| SPGET_OBJECTTYPE | one of MAPI_STORE, MAPI_FOLDER, MAPI_MESSAGE, MAPI_ATTACH |
| SPGET_FOLDERTYPE | one of FOLDER_ROOT, FOLDER_GENERIC, FOLDER_SEARCH |
| SPGET_PARENTEID | the EntryID of the node's parent (nidParent) |
| SPGET_FOLDERID | PidTagReplItemid of the containing folder's PC |
| SPGET_TABLE_ONLY | read from the row of the parent folder's hierarchy, contents or FAI contents table: PidTagReplItemid, PidTagReplChangenum, PidTagReplVersionHistory, PidTagReplFlags, PidTagReplCopiedfromVersionhistory, PidTagReplCopiedfromItemid |
| SPGET_PROV_ITEMID | PidTagProviderItemid or PidTagProviderParentItemid with the bytes swapped (endian-swapped) |
| SPGET_HASATTACH | 1 if PidTagMessageFlags has mfHasAttach, else 0 |
| SPGET_MSGSTATUS | PidTagMessageStatus from the parent folder's contents table row |
| SPGET_STORESTATE | STORE_HAS_SEARCHES if a node with NID_SEARCH_ACTIVITY_LIST exists, else 0 |
| SPGET_CONTENT_COUNT, SPGET_UNREAD_COUNT | PidTagContentCount (or ContentUnreadCount) minus PidTagPstHiddenCount (or PidTagPstHiddenUnread) when the hidden property exists |
| SPGET_HST_FOLDERREPL | true if PidTagContainerClass is IPF.Note, IPF.Contact, IPF.Appointment or empty |
| SPGET_IPMSUBTREE_DESC, SPGET_SUBTREE_CONTAINER | whether the node is under the IPM subtree; SUBTREECONTAINER_NONE or SUBTREECONTAINER_IPM_SUBTREE |
| SPGET_SUBJECT, SPGET_SUBJECTPREFIX, SPGET_NORMALIZESUBJECT | the subject, prefix or normalised subject in the requested string type (below) |
| SPGET_DISPLAY | the base-tag property if it exists, else an empty string of the requested encoding |
| SPGET_BB_BODY | the best available body in the requested format (PidTagBody, PidTagHtml or PidTagRtfCompressed) |
| SPGET_BB_PROPTAG | the tag of the best body: usual order PidTagRtfCompressed (only if PidTagRtfInSync is true), PidTagHtml, PidTagBody; the best body can be set explicitly to override the order |
| SPGET_RTF_IN_SYNC, SPGET_RTF_AUX | true if the RTF body exists and matches the other bodies; the RTF auxiliary properties (PidTagRtfSyncBodyCrc, BodyCount, BodyTag, PrefixCount, TrailingCount), synchronising the RTF first if it is not in sync |
| SPGET_BODYHTMLA | ANSI PST only: PidTagHtml if present, else an HTML rendering generated from the other bodies, in ANSI |
| SPGET_CONVERSATION_ID | the conversation ID computed from PidTagConversationIndex, PidTagConversationTopic and PidTagConversationIndexTracking (the algorithm is in section 5.4 of [MS-PST]) |
Set and delete behaviours
| Code | Effect |
|---|---|
| SPSET_MF | refreshes PidTagMessageFlags from the state of the message (its bodies, recipients, attachments, and so on) |
| SPSET_SP, SPSET_SUB | set the subject prefix or the subject, which may change the other subject properties |
| SPSET_MC | sets the message class; invalid classes must be rejected |
| SPSET_RA | sets an RTF auxiliary property; all five must stay synchronised |
| SPSET_RIS | writes PidTagRtfInSync, with possible effects on the best-body properties |
| SPSET_BBB | sets a body property and makes that format the best body |
| SPSET_CID | sets the property and recalculates PidTagConversationId |
| SPSET_FOLDERID | sets PidTagReplItemid of the containing folder |
| DEL_1 (SPDEL_1) | delete from the PC; must update PidTagSubject and PidTagNormalizedSubject |
| DEL_2 | delete from the PC; must update PidTagBody, PidTagRtfCompressed, PidTagRtfInSync, PidTagHtml and, in an ANSI PST, PidTagBodyHtml |
| DEL_3 | delete from the PC; may need to update the RTF auxiliary properties |
| DEL_4 | succeeds but does not delete |
| DEL_5 | delete, with an effect on PidTagConversationId |
| N, PC | (delete) not allowed; delete from the PC with no side effects |
Subject handling
PidTagSubject is stored with optional metadata. If its first character (one byte in an ANSI PST, two in a Unicode one) has the value 1, the next character is the length of the prefix plus one (the prefix and its separator, usually a space); the normalised subject follows the prefix. The specification's ANSI example is the bytes 01 05 52 45 3A 32 53 55 42 00: the length field is 5 for a 4-character prefix (52 45 3A 32), so the normalised subject (SUB) starts at index 6. When writing, add 1 to the length. If the first character is not 1 the string is the whole subject with no metadata, and the prefix and normalised subject must be found by parsing: a prefix is one to three non-space, non-digit characters followed by a colon, then zero or more space characters (no other white space) before the normalised subject.
Calculated properties by object
Columns: tag, base tag if any, Get, Set, Delete, List. (ANSI and Unicode lists are the same except where the specification marks ANSI only.)
| Object | Property | Base | Get | Set | Del | List |
|---|---|---|---|---|---|---|
| Store | PidTagReplFlags | PC | PC | PC | ALLOW | |
| Store | PidTagAssociatedSharingProvider | PC | PC | PC | ALLOW | |
| Store | PidTagMappingSignature | SPGET_MAPSIG | N | N | ALWAYS | |
| Store | PidTagRecordKey | SPGET_UIDRESOURCE | N | N | ALLOW | |
| Store | PidTagStoreRecordKey | SPGET_UIDRESOURCE | N | N | ALWAYS | |
| Store | PidTagStoreEntryId, PidTagEntryId | SPGET_STOREEID | N | N | ALWAYS | |
| Store | PidTagObjectType | nid | SPGET_OBJECTTYPE | N | N | ALWAYS |
| Store | PidTagDisplayName | PC | PC | N | ALLOW | |
| Store | PidTagStoreState | SPGET_STORESTATE | N | N | ALWAYS | |
| Store | PidTagStoreProvider | SPGET_UIDPROVIDER | N | N | ALWAYS | |
| Store | PidTagReceiveFolderSettings | SPGET_TRUE | N | N | ALWAYS | |
| Folder | PidTagParentEntryId | nidParent | SPGET_PARENTEID | N | N | ALWAYS |
| Folder | PidTagReplItemid, ReplChangenum, ReplVersionhistory, ReplFlags, ReplCopiedfromVersionhistory, ReplCopiedfromItemid | SPGET_TABLE_ONLY | N | N | NEVER | |
| Folder | PidTagReplFolderid | nidParent | SPGET_FOLDERID | SPSET_FOLDERID | N | NEVER |
| Folder | PidTagProviderItemid | nid | SPGET_PROV_ITEMID | N | N | NEVER |
| Folder | PidTagMappingSignature, StoreRecordKey, StoreEntryId, StoreProvider | as for the store | N | N | ALWAYS | |
| Folder | PidTagRecordKey | nid | SPGET_RECORDKEY | N | N | ALWAYS |
| Folder | PidTagObjectType | nid | SPGET_OBJECTTYPE | N | N | ALWAYS |
| Folder | PidTagEntryId | nid | SPGET_EID | N | N | ALWAYS |
| Folder | PidTagAttributeHidden | PC | PC | PC | ALLOW | |
| Folder | PidTagDisplayName | PC | PC | N | ALLOW | |
| Folder | PidTagFolderType | nid | SPGET_FOLDERTYPE | N | N | ALWAYS |
| Folder | PidTagContentCount | PR_CONTENT_COUNT | SPGET_CONTENT_COUNT | N | N | ALLOW |
| Folder | PidTagContentUnreadCount | PR_CONTENT_UNREAD | SPGET_UNREAD_COUNT | N | N | ALLOW |
| Folder | PidTagSubfolders | PC | N | N | ALLOW | |
| Folder | PidTagContainerHierarchy, ContainerContents, FolderAssociatedContents | SPGET_TRUE | N | N | ALWAYS | |
| Folder | PidTagExtendedFolderFlags | PC | PC | PC | ALLOW | |
| Folder | PidTagShortTermParentEntryIdFromObject | nidParent | SPGET_PARENTEID | N | N | NEVER |
| Folder | PidTagShortTermEntryIdFromObject | nid | SPGET_EID | N | N | NEVER |
| Folder | PidTagPstIpmsubTreeDescendant | nid | SPGET_IPMSUBTREE_DESC | N | N | NEVER |
| Message | PidTagMessageClass | PC | SPSET_MC | N | ALLOW | |
| Message | PidTagSubject | PidTagSubject | SPGET_SUBJECT | SPSET_SP | DEL_1 | ALLOW |
| Message | PidTagSubjectPrefix | PidTagSubject | SPGET_SUBJECTPREFIX | SPSET_SUB | DEL_1 | BASED |
| Message | PidTagConversationTopic, PidTagConversationIndex | PC | SPSET_CID | DEL_5 | ALLOW | |
| Message | PidTagDisplayBcc | PidTagDisplayBcc | SPGET_DISPLAY | N | N | ALWAYS |
| Message | PidTagDisplayCc, PidTagDisplayTo | itself | SPGET_DISPLAY | PC | N | ALWAYS |
| Message | PidTagMessageFlags | PC | SPSET_MF | N | ALLOW | |
| Message | PidTagMessageSize | PC | PC | N | ALLOW | |
| Message | PidTagParentEntryId | nidParent | SPGET_PARENTEID | N | N | ALWAYS |
| Message | PidTagMessageRecipients, PidTagMessageAttachments | SPGET_TRUE | N | N | ALWAYS | |
| Message | PidTagMessageStatus | SPGET_MSGSTATUS | N | N | ALLOW | |
| Message | PidTagHasAttachments | PidTagMessageFlags | SPGET_HASATTACH | N | N | ALWAYS |
| Message | PidTagNormalizedSubject | PidTagSubject | SPGET_NORMALIZESUBJECT | N | N | BASED |
| Message | PidTagRtfInSync | SPGET_RTF_IN_SYNC | SPSET_RIS | SPDEL_4 | ALWAYS | |
| Message | PidTagReplItemid and the other Repl properties | SPGET_TABLE_ONLY | N | N | NEVER | |
| Message | PidTagReplFolderid | nidParent | SPGET_FOLDERID | SPSET_FOLDERID | N | NEVER |
| Message | PidTagProviderItemid, PidTagProviderParentItemid | nid, nidParent | SPGET_PROV_ITEMID | N | N | NEVER |
| Message | PidTagMappingSignature, RecordKey, StoreRecordKey, StoreEntryId, ObjectType, EntryId | as for the folder | N | N | ALWAYS | |
| Message | PidTagBody, PidTagRtfCompressed, PidTagHtml | SPGET_BB_BODY | SPSET_BBB | DEL_2 | BODY | |
| Message | PidTagBodyHtml (ANSI only) | PidTagHtml | SPGET_BODYHTMLA | SPSET_BBB | DEL_2 | (not listed) |
| Message | PidTagRtfSyncBodyCrc, BodyCount, BodyTag, PrefixCount, TrailingCount | SPGET_RTF_AUX | SPSET_RA | DEL_3 | NEVER | |
| Message | PidTagCreationTime, LastModificationTime, SearchKey | PC | PC | N | ALLOW | |
| Message | PidTagConversationId | SPGET_CONVERSATION_ID | N | DEL_5 | ALLOW | |
| Message | PidTagConversationIndexTracking | PC | SPSET_CID | DEL_5 | ALLOW | |
| Message | PidTagStoreProvider | SPGET_UIDPROVIDER | N | N | ALWAYS | |
| Message | PidTagContentFilterSpamConfidenceLevel | SPGET_CONTENT_FILTER_SCL | N | PC | NEVER | |
| Message | PidTagSecureSubmitFlags | SPGET_SECURE_SUBMIT_FLAGS | N | PC | NEVER | |
| Message | PidTagPstBestBodyProptag | SPGET_BB_PROPTAG | N | N | NEVER | |
| Message | PidTagShortTermParentEntryIdFromObject, ShortTermEntryIdFromObject | nidParent, nid | SPGET_PARENTEID, SPGET_EID | N | N | NEVER |
| Message | PidTagPstSubTreeContainer | nid | SPGET_SUBTREE_CONTAINER | N | N | NEVER |
| Attachment | PidTagAttachSize | PC | N | N | ALLOW | |
| Attachment | PidTagAttachNumber | nid | SPGET_LONGNID | N | N | ALWAYS |
| Attachment | PidTagMappingSignature, StoreRecordKey, StoreEntryId | as for the store | N | N | ALWAYS | |
| Attachment | PidTagRecordKey | nid | SPGET_RECORDKEY | N | N | ALWAYS |
| Attachment | PidTagObjectType | nid | SPGET_OBJECTTYPE | N | N | ALWAYS |
| Attachment | PidTagAttachMethod, PidTagRenderingPosition | PC | PC | N | ALLOW | |
| Attachment | PidTagSecureSubmitFlags | SPGET_SECURE_SUBMIT_FLAGS | N | PC | NEVER |
An embedded message has the same list as a message except that it has no PidTagMessageStatus, PidTagRtfInSync, Repl*, ProviderItemid, PidTagReplFolderid or PidTagShortTerm… and PidTagSubTreeContainer entries, and its PidTagSubject and PidTagSubjectPrefix have the Set codes exchanged relative to the message list (the specification lists SPSET_SUB for the subject and SPSET_SP for the prefix there).