Even if the API is fully supported, there’s more to using Hull than this. We recommend you consume the API through a Hull client or Connector. Those are easy to build and they package the entire request/response flow for you, subscribe to realtime updates from the platform and more.
Read a the 5 minutes intro on connectors.
The Hull API is a Flat API. You access objects by their Object ID, like this:
GET /:id
The Hull API lets you retrieve, add, modify and remove application data. We use standard HTTP methods for each of those actions.
GET
to retrieve,POST
to create,PUT
to modifyDELETE
to remove.GET /api/v1/51122a9841a1bc52b4000006
Raw HTTP
Hull.api('/51122a9841a1bc52b4000006', function(response){
console.log('response')
});
Javascript
/api/v1
. You can omit this prefix when using the Hull SDKs. We’ll add it for you.http://ORGANIZATION_NAMESPACE.hullapp.io/api/v1/RESOURCE
where ORGANIZATION_NAMESPACE
is the namespace of your organization.You authenticate to the API by providing your Connector ID and Secret in the request. You can find it in any connector settings pane.
Additionally you can authenticate “As a User” or “As an Account” by passing a JSON web token in Hull-Access-Token
- This is the way you can submit Events
and Attributes
on a given user. Checkout Identity Resolution for information on how to generate this JWT
Hull.js
, the library passes this JWT token in every call. You have limited access to the API, and can only perform tracking calls, traits, and call the Alias method.curl -X DELETE http://ORGANIZATION_NAMESPACE.hullapp.io/api/v1/:id \
-H "Hull-App-Id: CONNECTOR_ID" \
-H "Hull-Access-Token: JWT_OR_CONNECTOR_SECRET" \
From a server, set up Hull-App-Id
and Hull-Access-Token
HTTP headers to provide the Connector Id and Access Token.
The Hull-Access-Token
is either the Connector Secret you can find in your Connector’s Settings, or a User or Account-scoped JSON Web token generated by the library.
The Hull Node library handles all of this for you
curl -X DELETE http://ORGANIZATION_NAMESPACE.hullapp.io/api/v1/:id?id=CONNECTOR_ID&secret=JWT_OR_CONNECTOR_SECRET
You can also send your App ID and the Access Token as parameters, although we STRONGLY discourage it.
Beware to never expose your CONNECTOR_SECRET on the client side, only use these authenticated methods from your own server !
When sending data to the firehose, authentication happens in each header
object in the array that you will send.
You need to create and pass a JWT in headers["Hull-Access-Token"]
for each entry in the batch
array. This JWT will include the claims for the command you send.
{
"batch": [
{ "headers": { "Hull-Access-Token": "JWT_WITH_CLAIMS_1" }, "body": { PAYLOAD_1 } },
{ "headers": { "Hull-Access-Token": "JWT_WITH_CLAIMS_2" }, "body": { PAYLOAD_2 } },
...
]
}
Hull conform to standards regarding HTTP status codes to indicate success or failure of a request:
200
: OK, The requested object exist.201
: Created, the object has been created.400
: Bad Request, required parameters are missing.401
: Unauthorized, Your App ID, Access Token and/or User ID are missing or invalid.404
: Not Found, the requested object doesn’t exist.405
: Method Not Allowed, the called method doesn’t exist.500
, 503
: Server Errors, something went wrong on our side.GET /api/v1/51122a9841a1bc52b4000006/comments?per_page=10&page=2
curl
Hull.api('/api/v1/51122a9841a1bc52b4000006/comments', {
"per_page":10,
"page":2
},function(response){
console.log('response')
});
Javascript
Any request that returns a collection has a page based pagination.
To navigate through the pages, add a per_page
and a page
parameter to your request.
per_page
, number of items to display per page. 30
by default.page
, the page to retrieve. 1
by default.Each of your Users is defined by this object. This is a central part of the API, since most calls expect a User to be logged in to associate the action.
To Update user attributes, send data through the Firehose. See the section Bulk Operations below
ACCESS: Admin
POST https://hull-demos.hullapp.io/api/v1/search/user_report
EXAMPLE REQUEST
Hull.api('/search/user_report', 'post',{
"query": {
"bool": {
"filter": [
{
"terms": {
"email": [
"user@host.com"
]
}
},
{
"terms": {
"external_id": [
"1234"
]
}
}
]
}
},
"sort": {
"created_at": "asc"
},
"raw": true,
"page": 1,
"per_page": 2
}).then(function(response) {
console.log(response);
});
curl -X POST /search/user_report
-H Hull-App-Id='5113eca4fc62d87574000096' \
-d query='["{", " \"query\": {", " \"bool\": {", " \"filter\": [", " {", " \"terms\": {", " \"email\": [", " \"user@host.com\"", " ]", " }", " },", " {", " \"terms\": {", " \"external_id\": [", " \"1234\"", " ]", " }", " }", " ]", " }", " }", "}"]' \
-d sort='["{", " \"sort\": {", " \"created_at\": \"asc\"", " }", "}"]' \
-d raw='true' \
-d page='1' \
-d per_page='2' \
Performs an Elasticsearch Query on the User database. You can filter based on Events with the with_child
predicate. The Dashboard will let you explore queries. You can see them with the Chrome Console.
queryrequiredHash
Elasticsearch Query. See the Elasticsearch Query DSL for more info on combining search filters, See Search Templates for more examples
sortrequiredHash
Sort direction
rawrequiredBoolean
Raw format (only this is supported today)
pagerequirednumber
Page number
per_pagerequiredString
Items per page
GET https://hull-demos.hullapp.io/api/v1/users_segments
EXAMPLE REQUEST
Hull.api('/users_segments', 'get').then(function(response) {
console.log(response);
});
curl -X GET /users_segments
-H Hull-App-Id='5113eca4fc62d87574000096' \
Returns all the Users segments for this organization
POST https://hull-demos.hullapp.io/api/v1/users_segments
EXAMPLE REQUEST
Hull.api('/users_segments', 'post',{
"name": null,
"query": null,
"predicate": null
}).then(function(response) {
console.log(response);
});
curl -X POST /users_segments
-H Hull-App-Id='5113eca4fc62d87574000096' \
-d name='null' \
-d query='null' \
-d predicate='null' \
Creates and returns a new Users segment
namerequiredString
Segment name
queryrequiredHash
Elasticsearch query
predicaterequiredHash
Query in predicates format
GET https://hull-demos.hullapp.io/api/v1/settings/user_traits
EXAMPLE REQUEST
Hull.api('/settings/user_traits', 'get').then(function(response) {
console.log(response);
});
curl -X GET /settings/user_traits
-H Hull-App-Id='5113eca4fc62d87574000096' \
Returns all the traits for this organization
GET https://hull-demos.hullapp.io/api/v1/settings/user_traits/:attribute
EXAMPLE REQUEST
Hull.api('/settings/user_traits/foo', 'get').then(function(response) {
console.log(response);
});
curl -X GET /settings/user_traits/foo
-H Hull-App-Id='5113eca4fc62d87574000096' \
Get the attribute definition
attributerequired
The parameter name
PUT https://hull-demos.hullapp.io/api/v1/settings/user_traits/:attribute
EXAMPLE REQUEST
Hull.api('/settings/user_traits/foo', 'put').then(function(response) {
console.log(response);
});
curl -X PUT /settings/user_traits/foo
-H Hull-App-Id='5113eca4fc62d87574000096' \
Update the given attribute. Create a new attribute if not found.
attributerequired
The parameter name
typeString
(Optional) update attribute type. Allowed values: (boolean|date|numeric|string)
visibleBoolean
(Optional) update attribute visible. Set visible false will delete the attribute from user reports.
track_changesBoolean
(Optional) update attribute track_changes. Set track_changes false will prevent changes to emit attributes_changed event.
To Update account attributes, send data through the Firehose. See the section Bulk Operations below
ACCESS: Admin
POST https://hull-demos.hullapp.io/api/v1/search/account_report
EXAMPLE REQUEST
Hull.api('/search/account_report', 'post',{
"query": {
"bool": {
"filter": [
{
"terms": {
"domain": [
"host.com"
]
}
},
{
"terms": {
"external_id": [
"1234"
]
}
}
]
}
},
"sort": {
"created_at": "asc"
},
"raw": true,
"page": 1,
"per_page": 2
}).then(function(response) {
console.log(response);
});
curl -X POST /search/account_report
-H Hull-App-Id='5113eca4fc62d87574000096' \
-d query='["{", " \"query\": {", " \"bool\": {", " \"filter\": [", " {", " \"terms\": {", " \"domain\": [", " \"host.com\"", " ]", " }", " },", " {", " \"terms\": {", " \"external_id\": [", " \"1234\"", " ]", " }", " }", " ]", " }", " }", "}"]' \
-d sort='["{", " \"sort\": {", " \"created_at\": \"asc\"", " }", "}"]' \
-d raw='true' \
-d page='1' \
-d per_page='2' \
Performs an Elasticsearch Query on the User database. You can filter based on Events with the with_child
predicate. The Dashboard will let you explore queries. You can see them with the Chrome Console.
queryrequiredHash
Elasticsearch Query. See the Elasticsearch Query DSL for more info on combining search filters, See Search Templates for more examples
sortrequiredHash
Sort direction
rawrequiredBoolean
Raw format (only this is supported today)
pagerequirednumber
Page number
per_pagerequiredString
Items per page
GET https://hull-demos.hullapp.io/api/v1/accounts_segments
EXAMPLE REQUEST
Hull.api('/accounts_segments', 'get').then(function(response) {
console.log(response);
});
curl -X GET /accounts_segments
-H Hull-App-Id='5113eca4fc62d87574000096' \
Returns all the Accounts segments for this organization
POST https://hull-demos.hullapp.io/api/v1/users_segments
EXAMPLE REQUEST
Hull.api('/users_segments', 'post',{
"name": null,
"query": null,
"predicate": null
}).then(function(response) {
console.log(response);
});
curl -X POST /users_segments
-H Hull-App-Id='5113eca4fc62d87574000096' \
-d name='null' \
-d query='null' \
-d predicate='null' \
Creates and returns a new Accounts segment
namerequiredString
Segment name
queryrequiredHash
Elasticsearch query
predicaterequiredHash
Query in predicates format
GET https://hull-demos.hullapp.io/api/v1/settings/account_traits
EXAMPLE REQUEST
Hull.api('/settings/account_traits', 'get').then(function(response) {
console.log(response);
});
curl -X GET /settings/account_traits
-H Hull-App-Id='5113eca4fc62d87574000096' \
Returns all the attributes for this organization
GET https://hull-demos.hullapp.io/api/v1/settings/account_traits/:attribute
EXAMPLE REQUEST
Hull.api('/settings/account_traits/foo', 'get').then(function(response) {
console.log(response);
});
curl -X GET /settings/account_traits/foo
-H Hull-App-Id='5113eca4fc62d87574000096' \
Get the attribute definition
attributerequired
The parameter name
PUT https://hull-demos.hullapp.io/api/v1/settings/account_traits/:attribute
EXAMPLE REQUEST
Hull.api('/settings/account_traits/foo', 'put').then(function(response) {
console.log(response);
});
curl -X PUT /settings/account_traits/foo
-H Hull-App-Id='5113eca4fc62d87574000096' \
Update the given attribute. Create a new attribute if not found.
attributerequired
The parameter name
typeString
(Optional) update attribute type. Allowed values: (boolean|date|numeric|string)
visibleBoolean
(Optional) update attribute visible. Set visible false will delete the attribute from user reports.
track_changesBoolean
(Optional) update trait track_changes. Set track_changes false will prevent changes to emit attributes_changed event.
To send user events, send data through the Firehose. See the section Bulk Operations below
ACCESS: Admin
POST https://hull-demos.hullapp.io/api/v1/search/event
EXAMPLE REQUEST
Hull.api('/search/event', 'post',{
"query": {
"bool": {
"filter": [
{
"has_parent": {
"parent_type": "user_report",
"query": {
"terms": {
"email": [
"romain@hull.io"
]
}
}
}
},
{
"terms": {
"event": [
"Added Tag"
]
}
}
]
}
},
"sorting": {
"created_at": "asc"
},
"raw": true,
"page": 1,
"per_page": 2
}).then(function(response) {
console.log(response);
});
curl -X POST /search/event
-H Hull-App-Id='5113eca4fc62d87574000096' \
-d query='["{", " \"query\": {", " \"bool\": {", " \"filter\": [", " {", " \"has_parent\": {", " \"parent_type\": \"user_report\",", " \"query\": {", " \"terms\": {", " \"email\": [", " \"romain@hull.io\"", " ]", " }", " }", " }", " },", " {", " \"terms\": {", " \"event\": [", " \"Added Tag\"", " ]", " }", " }", " ]", " }", " }", "}"]' \
-d sorting='["{", " \"sorting\": {", " \"created_at\": \"asc\"", " }", "}"]' \
-d raw='true' \
-d page='1' \
-d per_page='2' \
Performs an Elasticsearch Query on the Event data. You can filter based on Users and their associated accounts with the with_parent
predicate. The Dashboard will let you explore queries. You can see them with the Chrome Console
queryrequiredHash
Elasticsearch Query. Use has_parent
and terms
queries to search, See Search Templates for more examples
sortingrequiredHash
Sort direction
rawrequiredBoolean
Raw format (only this is supported today)
pagerequirednumber
Page number
per_pagerequiredString
Items per page
GET https://hull-demos.hullapp.io/api/v1/search/event/bootstrap
EXAMPLE REQUEST
Hull.api('/search/event/bootstrap', 'get').then(function(response) {
console.log(response);
});
curl -X GET /search/event/bootstrap
-H Hull-App-Id='5113eca4fc62d87574000096' \
Returns the list of all detected User Events and their properties
GET https://hull-demos.hullapp.io/api/v1/status
EXAMPLE REQUEST
Hull.api('/status', 'get').then(function(response) {
console.log(response);
});
curl -X GET /status
-H Hull-App-Id='5113eca4fc62d87574000096' \
Returns active jobs and current status for all connectors
GET https://hull-demos.hullapp.io/api/v1/:app_id/status
EXAMPLE REQUEST
Hull.api('/5d2edbc21f12e7e572000b9c/status', 'get').then(function(response) {
console.log(response);
});
curl -X GET /5d2edbc21f12e7e572000b9c/status
-H Hull-App-Id='5113eca4fc62d87574000096' \
Returns the status of a connector
PUT https://hull-demos.hullapp.io/api/v1/:app_id/status
EXAMPLE REQUEST
Hull.api('/5d2edbc21f12e7e572000b9c/status', 'put',{
"status": "ok|warning|error|setupRequired",
"messages": [
"missing credentials"
]
}).then(function(response) {
console.log(response);
});
curl -X PUT /5d2edbc21f12e7e572000b9c/status
-H Hull-App-Id='5113eca4fc62d87574000096' \
-d status='ok|warning|error|setupRequired' \
-d messages='["missing credentials"]' \
Update the status of a connector
statusrequiredBoolean
The Status code for the connector
messagesrequiredArray
An array of human-readable strings to explain the status
The Hull API exposes 2 ways to import data:
input records
such as track
, traits
, alias
, unalias
, and relies on the individual headers
of each entry to resolve the User or Account to update.When sending data to the firehose, authentication happens in each header
object in the array that you will send. You need to create and pass a JWT in headers["Hull-Access-Token"]
for each entry in the batch
array. This JWT will include the claims for the command you send. For more details, see the section on building JWT Tokens
Here’s a summary of how the Tokens are created:
iss
: Identifier of the issuer of the token (Your Hull Connector ID)iat
: Unix timestamp of the date the token has been issued atio.hull.subjectType
: The entity to apply the input record to. Possible values: "user"|"account"
exp
: Unix timestamp of the expiration date for the token. After this date, the token will not be considered valid.nbf
: Unix timestamp of a date. Before this date, the token will not be considered valid.io.hull.active
: Mark the User “Active”. Default: false
io.hull.create
: If not found, create the user (What you want most of the time). Default: true
io.hull.asUser
: Object with Identifiers. resolves the record to this userio.hull.asAccount
: Object with Identifiers. resolves the record to this account.const claim = {
iss: CONNECTOR_ID,
iat: UnixTimeStamp,
nbf: UnixTimeStamp,
exp: UnixTimeStamp,
"io.hull.asUser": { anonymous_id: "1234", external_id: "ad56e587fw9e8", domain: "foo.com" },
"io.hull.create": true,
"io.hull.active": false,
"io.hull.subjectType": "user"
}
const token = jwt.encode(claim, CONNECTOR_SECRET)
For each input record, you must send an entry in the batch
array with the following structure:
"batch": [
{
"type": "traits",
"timestamp": "2017-03-24T11:06:40.150Z",
"headers": {
// In the JWT, you need to encode the `User` and/or `Account` claims,
// such as `email`, `external_id`, etc...
"Hull-Access-Token": "JWT_ENCODED_CLAIMS"
},
"body": {
// For a Traits call, body will contain all the attributes to update
"foo": "bar"
}
}
]
"batch": [
{
"type": "track",
"timestamp": "2017-03-24T11:06:40.150Z",
"headers": {
// In the JWT, you need to encode the `User` and/or `Account` claims,
// such as `email`, `external_id`, etc...
"Hull-Access-Token": "JWT_ENCODED_CLAIMS"
},
"body": {
// For a Track call, these fields are the ones we recognize
"ip": null,
"ip": null,
"url": null,
"referer": null,
// Pass the Event's Name in the `event` key
"event": "hello",
// Pass your custom event proeprties in the `properties` key.
"properties": {
"who": "world"
}
}
}
]
"batch": [
{
"type": "alias",
"timestamp": "2017-03-24T11:06:40.150Z",
"headers": {
"Hull-Access-Token": "JWT_TOKEN"
},
"body": {
"anonymous_id": "1234"
}
}
]
To link a User to an account, you need to create a special JWT_TOKEN
that will contain both the io.hull.asUser
and io.hull.asAccount
claims. Linking will be done by the platform based on how the user and the account were resolved.
const claim = {
iss: CONNECTOR_ID,
iat: UnixTimeStamp,
"io.hull.asUser": { anonymous_id: "abcd", external_id: "df46w8e7", domain: "user@foo.com" },
"io.hull.asAccount": { anonymous_id: "1234", external_id: "ad56e587fw9e8", domain: "foo.com" },
"io.hull.subjectType": "user"
}
const token = jwt.encode(claim, CONNECTOR_SECRET)
ACCESS: Admin
POST https://firehose.hullapp.io
EXAMPLE REQUEST
Hull.api('https://firehose.hullapp.io', 'post',{
"timestamp": "2017-03-24T11:06:40.150Z",
"sentAt": "2017-03-24T11:06:40.150Z",
"batch": [
{
"type": "track",
"timestamp": "2017-03-24T11:06:40.150Z",
"headers": {
"Hull-Access-Token": "JWT_TOKEN"
},
"body": {
"ip": null,
"url": null,
"referer": null,
"event": "hello",
"properties": {
"who": "world"
}
}
},
{
"type": "traits",
"timestamp": "2017-03-24T11:06:40.150Z",
"headers": {
"Hull-Access-Token": "JWT_TOKEN"
},
"body": {
"first_name": "Boby",
"last_name": "Lapointe"
}
},
{
"type": "alias",
"timestamp": "2017-03-24T11:06:40.150Z",
"headers": {
"Hull-Access-Token": "JWT_TOKEN"
},
"body": {
"anonymous_id": "1234"
}
}
]
}).then(function(response) {
console.log(response);
});
curl -X POST https://firehose.hullapp.io
-H Hull-Organization='example.hullapp.io' \
-H Hull-App-Id='57839d3b7ad8a801f10000cd' \
-H Hull-Access-Token='dfed7cb1038208a4e54b281a935cd743' \
-H Content-Type='application/json' \
-d timestamp='2017-03-24T11:06:40.150Z' \
-d sentAt='2017-03-24T11:06:40.150Z' \
-d batch='[{"type":"track","timestamp":"2017-03-24T11:06:40.150Z","headers":{"Hull-Access-Token":"JWT_TOKEN"},"body":{"ip":null,"url":null,"referer":null,"event":"hello","properties":{"who":"world"}}},{"type":"traits","timestamp":"2017-03-24T11:06:40.150Z","headers":{"Hull-Access-Token":"JWT_TOKEN"},"body":{"first_name":"Boby","last_name":"Lapointe"}},{"type":"alias","timestamp":"2017-03-24T11:06:40.150Z","headers":{"Hull-Access-Token":"JWT_TOKEN"},"body":{"anonymous_id":"1234"}}]' \
Ingests Events and Attributes for multiple user & accounts. Our Node libraries manage this automatically. When Batching, the claims need to each be encoded as a separate JWT and passed in Headers as `Hull-Access-Token`
Hull-Organizationrequired
Your Organization ID
Hull-App-Idrequired
The connector's ID
Hull-Access-Tokenrequired
The connector's Secret
Content-Typerequired
Content Type: must be application/json
timestamprequiredDate
Timestamp
sentAtrequiredDate
Timestamp
batchrequiredString
An Array of objects
ACCESS: Admin
POST https://hull-demos.hullapp.io/api/v1/import/users
EXAMPLE REQUEST
Hull.api('/import/users', 'post',{
"url": "https://example.to/import/file.json",
"format": "json",
"overwrite": false,
"notify": false,
"emit_event": false,
"name": "csv_import.csv",
"description": "First Import",
"traits": "{ additional: true }"
}).then(function(response) {
console.log(response);
});
curl -X POST /import/users
-H Hull-App-Id='5113eca4fc62d87574000096' \
-d url='https://example.to/import/file.json' \
-d format='json' \
-d overwrite='false' \
-d notify='false' \
-d emit_event='false' \
-d name='csv_import.csv' \
-d description='First Import' \
-d traits='{ additional: true }' \
Imports a JSON file of users, updating or creating them. Checkout implementation details and tutorial
urlrequired
The URL of the file to import.
formatrequired
File format, Only json
is supported for now
overwriteoptional
Will the import have precedence over existing data if a matching user is found. If False, data in Hull wins in case of a field conflict. Default: false
notifyoptional
Notify connectors of each user that was imported, and trigger them accordingly. Default: false
emit_eventoptional
If true, will we emit a Imported User
event for each user in the list, with the following properties:
- each property found in the traits
parameter
- job_id
,
- job_name
, job_description
: values of the name
and description
parameters
- action
: updated
if the User was found and updated, new
if he was created.
nameoptional
Name to show in the emitted event. Default: filename from URL
descriptionoptional
Optional Description. Default: ''
traitsoptional
A hash of properties to apply to every user in this list - user data in list takes precedence. default: {}
schedule_atoptional
Schedule the import to be run at a future date
ACCESS: Admin
POST https://hull-demos.hullapp.io/api/v1/extract/users
EXAMPLE REQUEST
Hull.api('/extract/users', 'post',{
"url": "https://example.to/webhooks/callback",
"format": "json",
"query": "{ Elasticsearch query }"
}).then(function(response) {
console.log(response);
});
curl -X POST /extract/users
-H Hull-App-Id='5113eca4fc62d87574000096' \
-d url='https://example.to/webhooks/callback' \
-d format='json' \
-d query='{ Elasticsearch query }' \
Starts a Bulk users extraction, generates a file and ping the URL of your choice with its location. Checkout the Intercom Connector Extract method to see how we suggest to use this.
urlrequired
The URL to call when extract is ready.
formatrequired
File format, "csv" and "json" are supported
fieldsoptional
If export is CSV, which attributes to include. in JSON exports, everything is included.
queryrequired
An Elasticsearch query to define who to include in the export. Hint: Build your query in the Dashboard and copy it from the console to make it easier to build. In Notifications, segments also include a copy of the query that defines them (notification.segments[0].query
- you can use it.
ACCESS: Admin
POST https://hull-demos.hullapp.io/api/v1/extract/accounts
EXAMPLE REQUEST
Hull.api('/extract/accounts', 'post',{
"url": "https://example.to/webhooks/callback",
"format": "json",
"query": "{ Elasticsearch query }"
}).then(function(response) {
console.log(response);
});
curl -X POST /extract/accounts
-H Hull-App-Id='5113eca4fc62d87574000096' \
-d url='https://example.to/webhooks/callback' \
-d format='json' \
-d query='{ Elasticsearch query }' \
Starts a Bulk accounts extraction, generates a file and ping the URL of your choice with its location. Checkout the Intercom Connector Extract method to see how we suggest to use this.
urlrequired
The URL to call when extract is ready.
formatrequired
File format, "csv" and "json" are supported
fieldsoptional
If export is CSV, which attributes to include. in JSON exports, everything is included.
queryrequired
An Elasticsearch query to define who to include in the export. Hint: Build your query in the Dashboard and copy it from the console to make it easier to build. In Notifications, segments also include a copy of the query that defines them (notification.segments[0].query
- you can use it.