Data Hub Custom Schemas

Overview

Data Hub is designed to store data from across your Bullhorn platform in one place, unlocking powerful new opportunities for reporting and analytics. Oftentimes, this data originates from non-Bullhorn applications that complement your recruitment workflow.

Read this article to find step-by-step guidance on how to create data definitions that enable you to send data from these external applications into the Data Hub for use in analytics and reporting.

Part 1: Getting Started

The Basics

The Data Hub uses JSON schemas to define its data structures. JSON is an industry standard that enables easy interoperability at scale.

This document assumes familiarity with JSON schemas. If this concept is new to you, please reach out to Bullhorn Professional Services for additional assistance.

Before you start building your first Data Hub schema, you'll need to gather some information.

First, define what entities and properties you want to store in the Data Hub.

Let's say we wanted to capture and analyze survey response data that was sent from an external application. Let's call this collection of data Survey. We would define a model for sending and storing this data into something that is roughly equivalent to a database table.

Other examples of data that could be stored in Data Hub might be "Referrals", "Timesheets", or a collection of financial data. Each of these would be defined with their own unique model or schema. The properties of each are roughly like columns in a table or a spreadsheet.

Moving forward, we'll refer to your collection of data as an "entity".

Data Types

Each field or property within your entity has a defined type. The following are the core data types supported within the Data Hub:

  • string
  • integer
  • number (aka decimal)
  • Boolean

Extra formats supported for string data type:

  • date-time
  • uuid

We will be defining a data type for each property within our entities to match one from the list above.

Navigate to Custom Attributes on Data Hub Schema Definitions for additional custom attributes.

Property Relationships

Each entity should have a relationship to one or more Bullhorn Entities. Examples of relationships that your entity may be related to include:

  • candidateId
  • clientCorporationId
  • clientContactId
  • jobOrderId
  • corporateUserId
  • leadId
  • opportunityId
  • placementId
  • appointmentId
  • noteId
  • payableChargeId
  • billableChargeId

Now that we understand the basics, let’s fill out the worksheet below with the properties of your first entity.

Defining your First Data Hub Custom Entity

Bullhorn strongly recommends you start with one simple entity from an external application with a few simple properties so you can become familiar with the entire process end to end. Once you’ve run an initial successful test, proceed with augmenting your schemas with more advanced configurations.

Custom Entity Basic Properties

Your Entity Name (no spaces): ____________________

Related to Bullhorn Entity: ____________________

Table 1: Your Custom Entity

FieldName User Friendly Label FieldType
Sample: surveyName Sample: Survey Name String
Sample: surveyName Sample: Survey Name String
Sample: surveyName Sample: Survey Name String

Sample Record (for testing)

Next, let’s define a sample record that will enable you to validate that the schema definition is correct. A sample record would simply represent the payload of your data with all fields and relationships.

For example, a sample record for our Survey entity might look like the example below. In this example, we are saying that we are creating a sample survey that includes:

  • A survey id of “123” (this value should represent the unique identifier from the source system that the data originated from)
  • A survey name of “Feedback on assignment”
  • A survey record is linked to a job that has an id of “4569900” and a candidate that has an ID of “7659900” in the ATS
  • Other properties for illustration

Sample 1 (the Payload portion):

Copy
{
    "sourceId": 123,
    "name": "Feedback on Assignment",
    "url": "https://bullhorn.com.test/mysurvey"
    "customerId": 4,
    "candidateID": 7659900,
    "bullhornJobID": 4569900
}

Sample 1 (as part of full API body):

In the above example, you’ll notice that the Payload was structured with backslashes to escape the double quotes so that it interprets the JSON entity within the JSON body of the API call correctly.

Additionally, notice the square brackets around both the entire body and items portion of the body - this is because each API call can contain a batch of records of different entity types so each sourceSystem and entity type can each contain a batch of items.

Copy
[{
  "sourceSystem": "HelloWorld",
    "entityType": "MyCustomObject",
    "schemaVersion": "1",
    "items": [{
        "sourceId": 123,
        "dateAddedInSourceSystem": "2023-11-27T15:05:57.7369920Z",
        "dateLastModifiedInSourceSystem": "2024-06-28T22:01:00.8857286Z",
        "isDeleted": false,
        "candidateId": 7659900,
        "jobOrderId": 4569900
        “Payload”: “{\"name\": \"Feedback on Assignment\",
        \"url\": \"https://bullhorn.com.test/mysurvey\",
        \"customerId\": 4,
        \"candidateID\": 7659900,
        \"bullhornJobID\": 4569900}”
    }]
}]

Enter a sample record for your entity in the table below, with one property for each of the rows you defined in your custom entity table above. You will use it later for testing that your schema is valid.

MySampleRecord:

Copy
{
 
















}

You are now ready to build your custom schema.

Building your Custom Schema

Each of the fields defined in Table 1 should also be added to the schema definition. For example, for our sample entity of “Survey”, a starter schema would look like this:

Be careful to validate all open/close brackets and quotes.

Sample Schema 1a: Survey - part 1

Copy
{
    "$schema": "https://json-schema.org/draft/2020-12/schema",
    "type": "object",
    "sourceIdType": "integer",
    "properties": {
     "Id": {
                "type": "integer",
            "label": "Id"
            },            
     "Url": {
            "type": "string",
            "label": "URL"
        },
        "Name": {
            "type": "string",
            "label": "Survey Name"
        },
            "CustomerId": {
                "type": "integer",
            "label": "Customer Id"
            },
            "DateCreated": {
                "type": "string",
                "format": "date-time"
        }
    }
}

Next, augment your schema by adding links to one or more Bullhorn entities as shown below. In this case, our record is associated with the Jobs entity in Bullhorn.

You can include the data under BullhornJobId for the ATS record linking in the payload, but the best practice is to include it outside of the payload itself. But if you define the schema with it as a property and include the promoteTo value correctly, it will also successfully reference the ATS entity.

Schema 1b

Copy
{
    "$schema": "https://json-schema.org/draft/2020-12/schema",
    "type": "object",
    "sourceIdType": "integer",
    "properties": {
        "Id": {
                "type": "integer",
            "label": "Id"
            },        
     "Url": {
            "type": "string",
            "label": "URL"
        },
        "Name": {
            "type": "string",
            "label": "Survey Name"
        },
            "CustomerId": {
                "type": "integer",
            "label": "Customer Id"
            },
            "DateCreated": {
                "type": "string",
                "format": "date-time"


        },
      "BullhornJobId": {
            "type": "integer",
            "promoteTo": "jobOrderId",
            "link": {
                "type": "ATS",
                "entity": "JobOrder"
            }
        }
    }
}

Perform the same steps for the properties from your custom entity table. Enter your custom schema below, using the data from Table 1. Ensure to include a relationship to one or more Bullhorn entities.

MyFirstSchema:

Copy
{












}

You're now ready to run some tests.

Creating Records in the Data Hub with Your Custom Schema

Register your Schema in the Data Hub

The schema definition should be documented so you can refer to it internally (like a specification document). Before you send data to Data Hub via the API, the schema definition must be registered and set up with Bullhorn. This process is initiated through a support ticket (refer to Step 7 in Adding External Data to the Data Hub). Bullhorn's support team will then set up the schema definitions that you have provided. Once completed, they will inform you that you can begin pushing data into the Data Hub.

Bullhorn's support team performs a basic validation of the schema to confirm that it’s valid JSON, but they are not responsible for understanding the specific data you are sending nor for validating that it is the “right” data for the metrics you are interested in receiving.

Create your First Record

Bullhorn recommends using Postman or an equivalent tool to interact with the Data Hub API using the Data Hub API Integration Reference Guide. The payload for your request should match your sample record “MySampleRecord” above.

Part 2: Advanced Use Cases

If one custom entity should reference another Data Hub entity, you can add metadata that defines where the linked record is stored. Supported link types are:

  • DATA_HUB for self-referential data. The metadata defines the sourceSystemName and entityTypeName they link to.
  • ATS for standard entities in the ATS. The metadata defines the ATS entity they link to e.g. JobOrder or Candidate.
  • EXTERNAL for links to external systems like Pardot, Sendgrid. The metadata defines a URL template string to be populated with the row ID.

Examples of linked records include:

SurveyQuestion

Copy
{
    "$schema": "https://json-schema.org/draft/2020-12/schema",
    "type": "object",
    "sourceIdType": "integer",
    "properties": {
        "Text": {
            "type": "string",
            "label": "Text"
        },
        "Scale": {
            "type": "string",
            "isType": true,
            "label": "Scale"
        },
        "DisplayOption": {
            "type": "string",
            "isType": true,
            "label": "Display Option"
        },
        "Survey_Id": {
            "type": "integer",
            "link": {
                "type": "DATA_HUB",
                "sourceSystem": "Automation",
                "entityType": "Survey"
            }
        },
        "Id": {
                "type": "integer",
                "deprecated": true
        }
}
}

Additional Examples of Custom Schemas/Records for Reference

  • Defines status as isType for fast filtering in Analytics. This is acceptable because status will not have a large number of unique values.
  • Uses the format of date-time for the paid_on field to indicate it should be treated as a date field instead of a normal string.
  • Defines retainer_fee as a monetary field with isMonetary and an explicit currency field of retainer_currency.
  • Defines extras_fee and total_fee as monetary fields without explicit currency fields, they will fallback to currency because it’s marked with isDefaultCurrency.
  • Links to the candidate entity in ATS with candidateId.
  • Links to a user entity in ATS with userId and has it marked with isOwner so that Analytics sees this as the overall.
  • Links to another Data Hub entity with automationSurveyId, an Automation Survey Submittal.
Copy
{
    "$schema": "https://json-schema.org/draft/2020-12/schema",
    "type": "object",
    "sourceIdType": "uuid",
    "properties": {
        "status": {
            "type": "string",
            "isType": true,
            "label": "Status"
        },
        "paid_on": {
            "type": "string",
            "format": "date-time",
            "label": "Paid On"
        },
        "retainer_fee": {
            "type": "number",
            "isMonetary": true,
            "currencyField": "retainer_currency",
            "label": "Retainer Fee"
        },
        "retainer_currency": {
            "type": "string",
            "isCurrencyField": true,
            "label": "Retainer Currency"
        },
        "extras_fee": {
            "type": "number",
            "isMonetary": true,
            "label": "Extras Fee"
        },
        "total_fee": {
            "type": "number",
            "isMonetary": true,
            "label": "Total Fee"
        },
        "currency": {
            "type": "string",
            "isCurrencyField": true,
            "isDefaultCurrency": true,
            "label": "Currency"
        },
        "candidateId": {
            "type": "integer",
            "label": "Candidate",
            "promoteTo": "candidateId",
            "link": {
                "type": "ATS",
                "entity": "Candidate"
            }
        },
        "userId": {
            "type": "integer",
            "isOwner": true,
            "label": "Owner",
            "promoteTo": "corporateUserId",
            "link": {
                "type": "ATS",
                "entity": "CorporateUser"
            }
        },
        "automationSurveyId": {
            "type": "integer",
            "label": "Automation Survey",
            "link": {
                "type": "DATA_HUB",
                "sourceSystem": "Automation",
                "entityType": "SurveySubmittal"
            }
        }
    }
}

For ATS Link Types, navigate to Bullhorn REST API Entity Names to see what allowable values are included:

REST Entity Name Reference Column (ID)
Candidate candidateID
Client Corporation clientCorporationId
Client Contact clientContactId
Job Order jobOrderId
CorporateUser corporateUserId
Lead leadId
Opportunity opportunityId
Placement placementId
Appointment appointmentId

Note

noteId

Payable Charge

payableChargeId

Billable Charge

billableChargeID