Schema
In this section, you will find all the information to create a JSON file that BloodHound can ingest and use to display your Nodes and Edges. The most up-to-date JSON Schema can always be found in our CE repository. Currently, the location of the node and edge schema files in our source code can be found here.Ingesting Generic Formatted Data
File Requirements
Acceptable formats:.json, .zip
You can mix file types in a single upload (e.g. Sharphound + Generic).
Compressed ZIPs containing multiple file types are supported.
JSON Format
The standard BloodHound UI upload screen now accepts files in a generic format. You can continue using it as before. At minimum, your JSON file should have these elements:nodes and edges must conform to our JSON Schema, see details below. The validation of the data occurs at upload time.
When ingest completes, the generic data will be available via Cypher search ONLY. Generic data is not searchable via the pathfinding feature (yet).
Entity Panels: clicking on a generic node or edge will only render the entity’s property bag. At this time there is no support for defining entity panels for generic entities.
Nodes
Property Rules
- Properties must be primitive types or arrays of primitive types.
- Nested objects and arrays of objects are not allowed.
- Arrays must be homogeneous (e.g. all strings or all numbers).
- An array of kind labels for the node. The first element is treated as the node’s primary kind and is used to determine which icon to display in the graph UI. This primary kind is only used for visual representation and has no semantic significance for data processing.
-
Property names must be lowercase. Property names in BloodHound are case-sensitive, which means
objectidandObjectIDare treated as two distinct properties on the same node. This can cause issues when parsing API responses in languages that treat JSON keys as case-insensitive (for example, PowerShell and some .NET-based tools).
Reserved Property: objectid
The property name objectid is reserved and must not be included in the properties object of a node definition.
In the BloodHound data model, the top-level id field serves as the unique identifier for the node.
Upon ingestion, this id value is automatically mapped and stored internally as the objectid property. Explicitly defining objectid within the properties map creates a redundant conflict that is unsupported. To ensure successful ingestion, remove any objectid keys from your node’s properties object and rely solely on the root-level id field for identification.
Node JSON
The following is the JSON schema that all nodes must conform to.Edges
Edgekind values must contain only letters (A-Z, a-z), numbers (0-9), and underscores (_).
BloodHound validates edge kinds at upload time. If any edge kind includes spaces, dashes (-), backticks, or other special characters, the file upload fails.
We recommend using PascalCase (for example, AdminTo, HasSession) for readability and consistency.
Neo4j Cypher allows many special characters in symbolic names when the name is enclosed in backticks. BloodHound OpenGraph ingest is more restrictive: edge kind values must match ^[A-Za-z0-9_]+$, so upload validation rejects backtick-escaped names, spaces, dashes, and other special characters.
See Neo4j Escaping rules for symbolic names for additional naming guidance.
Edge Kind Validation
BloodHound enforces this pattern for edge kinds:AdminToHasSessionAZMGGrantRoleEdge_1
Admin-ToHas SessionHas`SessionHas$Session
Edge Endpoint Matching
Edges in OpenGraph data define relationships between nodes using astart endpoint and an end endpoint. You can control how BloodHound resolves each endpoint using one of two matching strategies in the match_by field. This flexibility allows you to link nodes based on their unique database identifiers or by dynamically finding them based on specific attribute values.
Use identifier matching when possible. Property matching is more flexible, but it is slower and should be used only when you cannot match by node ID.
Match by Identifier
This is the default and most common method for defining edges. It resolves the endpoint by looking up a node using its unique internal ID or its human-readable name. To use this strategy, set thematch_by property to either "id" or "name".
"name" is deprecated and will be removed in future versions; using "property" with a single equality matcher is the recommended approach for name-based lookups.match_by: Set to"id"to match the node’s unique object identifier, or"name"to match the node’s name string.value: A required string containing the specific ID or name of the target node.kind(Optional): You may constrain the lookup to ensure the found node belongs to a specific type. For example, settingkind: "User"ensures that even if a name exists across multiple entity types, only the one classified as aUseris selected.property_matchers: Not used in this mode. If provided alongsidematch_by: "id", validation will fail.
Match by Property
Use this strategy when you do not know the unique ID of the target node but can identify it through one or more known attributes (e.g., a username, email address, hostname, or custom property). This method allows for dynamic resolution based on data available at ingestion time. To use this strategy, set thematch_by property to "property".
match_by: Must be set to"property".property_matchers: A required array of objects defining the criteria to find the node. Each object must include:key: The name of the node property to check.operator: Currently, only"equals"is supported.value: The expected value for the property (string, number, or boolean).- Note: You can provide multiple matchers in the array. The system will attempt to find a node that satisfies all conditions simultaneously.
value: Not used in this mode. Providing avaluefield whenmatch_byis"property"will cause validation errors.kind(Optional): Similar to identifier matching, you can restrict the search to nodes of a specific kind to avoid ambiguity.
username property and the server’s hostname property:
Edge JSON
The following is the JSON schema that all edges must conform to. If an edge kind does not meet the allowed pattern, BloodHound returns a schema validation error and rejects the upload.Post-processing
Post-processing in BloodHound refers to the analysis phase where the system creates certain edges after ingesting data to identify attack paths. After ingesting data, BloodHound analyzes the graph state and adds edges it considers useful. BloodHound regenerates “post-processed” edges after it builds a complete graph. Before regenerating post-processed edges, BloodHound deletes any existing ones. As a result, BloodHound removes any post-processed edges that you add directly to an OpenGraph payload.Show post-processed edges
Show post-processed edges
BloodHound creates the following edges during post-processing:
ADCSESC1ADCSESC3ADCSESC4ADCSESC6aADCSESC6bADCSESC9aADCSESC9bADCSESC10aADCSESC10bADCSESC13AddMemberAdminToAZAddOwnerAZMGAddMemberAZMGAddOwnerAZMGAddSecretAZMGGrantAppRolesAZMGGrantRoleAZRoleApproverCanPSRemoteCanRDPCoerceAndRelayNTLMToADCSCoerceAndRelayNTLMToLDAPCoerceAndRelayNTLMToLDAPSCoerceAndRelayNTLMToSMBDCSyncEnrollOnBehalfOfEnterpriseCAForExecuteDCOMExtendedByPolicyGoldenCertHasTrustKeysIssuedSignedByOwnsOwnsLimitedRightsProtectAdminGroupsSyncLAPSPasswordSyncedToADUserSyncedToEntraUserTrustedForNTAuthWriteOwnerWriteOwnerLimitedRights
AdminTo edge directly in your OpenGraph payload, BloodHound removes it during post-processing and the edge does not persist in the final graph as expected. Instead of adding AdminTo edges directly, include the supporting edges that cause the post-processor to generate the AdminTo edge. The common pattern that triggers the creation of the AdminTo edge is:
See the following example OpenGraph payload that produces the effect:
Optional Metadata Field
You can optionally include a metadata object at the top level of your JSON payload. This metadata currently supports a single field:source_kind: a string that applies to all nodes in the file, used to attribute a source to ingested nodes (e.g. Github, Snowflake, MSSQL). This is useful for tracking where a node originated. We internally use this concept already for AD/Azure, using the labels “Base” and “AZBase” respectively.
source_kind will be added to the kinds list of all nodes in the file during ingest. This feature is optional.
Minimal Working JSON
The following is a minimal example payload that conforms to the node and edge schemas above. You can use this as a starting point to build your own OpenGraph. Copy and paste the following example into a new.json file or download this example file.
Run:
