--- title: "Journey Hub API" slug: "journey-hub-api" updated: 2024-01-05T20:55:39Z published: 2024-01-05T20:55:39Z canonical: "help.csgi.com/journey-hub-api" --- > ## Documentation Index > Fetch the complete documentation index at: https://help.csgi.com/docs/llms.txt > Use this file to discover all available pages before exploring further. # Journey Hub API The **Journey Hub API** allows OEM organizations to automate the management of Organizations, Users, and Projects. ## Hub API Authentication Process 1. Generate a secret key (you must be a Journey Hub owner) by going to the admin section, clicking users, and selecting yourself from the left panel. Then you will see a button to "generate key". 2. On the same page, copy your user id. 3. Create the hash (see instructions below) 4. Create a 'POST' request to the /token route (see 'Request Object' format below) 5. The response returns a token and an expiration time. You can now include the token in the headers in order to access the user, org, and project routes. ## Hub API End Points - /token - /org - /project - /user [Full PDF documentation](https://cdn.document360.io/c578bb14-e570-4b6e-93ed-f6c5b8a2d8a1/Images/Documentation/Xponent%20Hub%20Provisioning%20API%201.0.pdf?sv=2026-02-06&spr=https&st=2026-10-26T14%3A19%3A12Z&se=2026-10-26T15%3A19%3A12Z&sr=c&sp=r&sig=p9H%2BCCxIeJChVf5kjYqNy4y61rsWLz8Ohy4JH78tevc%3D) gives the details for all of the endpoints. ## Creating Hash for Provisioning APIs In order to receive an authorization token to access the provisioning APIs, a user must submit an encrypted value based on their secret key, user id, and a user-defined salt/nonce. The user then passes the id, nonce/salt, and hash as an object in the request body to the 'POST: /token' route to receive a token. All other provisioning APIs are not accessible without a token. Values defined inside <> are meant to be filled in by the user with their credentials. For example, should replace the user's actual id. ```javascript { "userId": , "nonce": , "hash": } ``` ## How to create the hash by language **Cryptographic Hash:** SHA-256 **Return Value:** Hexadecimal representation of binary hash Do not reuse the same nonce/salt. Choose different values each time you call the '/token' route. ```javascript var crypto = require('crypto'); var hashSecret = function(secret, nonce) { var hash = crypto.createHash('sha256'); hash.update(secret + nonce); return hash.digest('hex'); } ``` ```ruby require 'digest' def hashSecret(secret, nonce) Digest::SHA256.hexdigest(secret + nonce) end ``` ```python import hashlib def hashSecret(secret, nonce): return hashlib.sha256(secret + nonce).hexdigest() ``` ```php ``` # Javascript Code Sample ```javascript /* * This code sample creates an new organization, user and project. * First, you need to get an authorization token using your * secret key from the UI (only admins can generate secret keys). * You must include the token in the headers to * access the user, org, and project routes if you have an authorization token. * Information on creating the hash for the /token route can be found at */ // library for making http calls var request = require('request'); // library to generate unique time based nonce for hashing secret var UUID = require('uuid-js'); // header object for requests var headers = { // authorization will be set after an authorization token is generated 'authorization': null, 'content-type': 'application/json' } // url for creating authorization token - replace with your end point var tokenPostURL = '' + '/api/v1/token'; // url to post users var userPostURL = '' + '/api/v1/user/org/'; // url to post a new organization var orgPostURL = '' + '/api/v1/org'; // url to post a project var projectPostURL = '' + '/api/v1/project/org/'; // url to put template connections in a project var projectPutURL = '' + '/api/v1/project/'; // credentials that need to be passed in order to get an authorization token // for more information on creating the hash see var credentials = { // user id must be passed in as a string id: '1', // time based uuid id as nonce nonce: UUID.create(1).toString(), hash: '' } // new organization information (to be used in the request body) // We are not passing in a parentId and will instead use the default var newOrganization = { name: 'Brand Spanking New Org' } // user array to add to request body var users = [ { name: 'Steve Owner', email: 'steve@owner.com', role: 'Owner' }, { name: 'Sally Member', email: 'sally@member.com', role: 'Member' }, { name: 'Jack Admin', email: 'jack@admin.com', role: 'Member' } ] // new project information to be added to request body var newProject = { name: 'Best Project Ever', description: 'This is the best project ever made :)', // you can retrieve template ids from the GET '/org/:org_id/templates' route templateId: '' } // construct the options passed as first parameter in all requests var createOptions = function(url, body, id) { return { url: id ? url + id : url, headers: headers, body: JSON.stringify(body) } } /* * AUTHORIZATION STEP: * Generate a token to access user, organization and project routes */ request.post(createOptions(tokenPostURL, credentials), function handleGenerateToken(err, res, body) { if(err || (res && res.statusCode !== 200)) { console.log(err || 'ERROR: ' + JSON.parse(body).metadata.message); return; } // convert the response to json var formattedTokenBody = JSON.parse(body).token; // set the token in the headers object headers.authorization = formattedTokenBody[0].token; /* * CREATE ORGANIZATION: * Post a new organization to host the project and users */ request.post(createOptions(orgPostURL, newOrganization), function handleCreatedOrg(err, res, body) { if(err || (res && res.statusCode !== 200)) { console.log(err || 'ERROR: ' + JSON.parse(body).metadata.message); return; } // extract the organization id from the response var formattedOrgBody = JSON.parse(body).organizations; var orgId = formattedOrgBody[0].id; console.log('Congrats! You just created an organization!'); /* * CREATE PROJECT: * create a project and add it to the newly created organization */ request.post(createOptions(projectPostURL, newProject, orgId), function handleCreatedProject(err, res, body) { if(err || (res && res.statusCode !== 200)) { console.log(err || 'ERROR: ' + JSON.parse(body).metadata.message); return; } console.log('Congrats! You just created a project!'); var formattedProjectBody = JSON.parse(body).projects; // extract the new project id var projectId = formattedProjectBody[0].id; // check if response contains template connections if(formattedProjectBody[0].templateConnections.length !== 0) { // extract the template connections from the response body // and format each connection into correct format for adding it to the project via a PUT var templateConnections = formattedProjectBody[0].templateConnections.map(function(connection) { return { id: connection.id, details: { type: connection.type } } }); /* * ADD TEMPLATE CONNECTIONS: * using the response from 'POST', you can now add template connections to the project */ request.put(createOptions(projectPutURL, { connections: templateConnections }, projectId), function handleTempConnections(err, res, body) { if(err || (res && res.statusCode !== 200)) { console.log(err || 'ERROR: ' + JSON.parse(body).metadata.message); return; } console.log('Congrats! You just added template connections to your project!'); }) } }); /* * ADD TEMPLATE CONNECTIONS: * add users to the newly created organization */ request.post(createOptions(userPostURL, users, orgId), function handleAddedUsers(err, res, body) { if(err || (res && res.statusCode !== 200)) { console.log(err || 'ERROR: ' + JSON.parse(body).metadata.message); return; } var formattedUserBody = JSON.parse(body).users; console.log('Congrats! You just added users to your organization!'); }); }); }); ``` ## Python Code Sample #### List the organizations: ```python ''' Sample Python Code for Xponent Journey Hub API ''' # Python Sample Code to create orgs etc import hashlib import uuid import json import requests # Your API secret here - from Admin -> users mysecret = 'mylongsecret' # Your user Id - as a string myId='1234567' nonce = str(uuid.uuid1()) def hashSecret(secret, nonce): return hashlib.sha256(secret + nonce).hexdigest() # header object for requests headers = { # authorization will be set after an authorization token is generated 'authorization': None, 'content-type': 'application/json' } credentials = { # user id must be passed in as a string 'userId' : myId, # time based uuid id as nonce 'nonce': nonce, 'hash' : hashSecret(mysecret,nonce) } # url for creating authorization token - replace with your end point url='http://localhost:3000/api/v1/' # Request the token using the calculate credentials r = requests.post(url+'token',data=credentials) if r.status_code <> 200 : raise Exception(str(r.text)) # Now we should have a token token = dict(r.json())['tokens'][0]['token'] # Get the organizations r = requests.get(url+'org',headers={'Authorization' : token }) if r.status_code <> 200 : raise Exception(str(r.text)) orgs=dict(r.json())['organizations'] # Print out the Orgs print "\nList of Organizations:\n" print "%20s\t%20s\t%20s" % ("Organization Name","Id","ParentId") for o in sorted(orgs, key=lambda k: k['id']) : print "%(name)20s\t%(id)20s\t%(parentId)20s" % o # Create a new Org r = requests.post(url+'org',headers={'Authorization':token}, data={ "name" : "neworg-"+str(uuid.uuid1())[:5], "parentId" : 1}) if r.status_code <> 200 : raise Exception(str(r.text)) newOrg = dict(r.json())['organizations'][0] print "\nSuccessfully created new Org %(name)s (%(id)s)" % newOrg # Change the name of the org r = requests.put(url+'org/'+newOrg['id'],headers={'Authorization':token}, data={ "name" : "changed-"+str(uuid.uuid1())[:5]}) if r.status_code <> 200 : raise Exception(str(r.text)) changeOrg = dict(r.json())['organizations'][0] print "\nSuccessfully changed org name %s to %s (%s) \n" % (newOrg['name'], changeOrg['name'], changeOrg['id']) # Add it to the list of orgs orgs.append(changeOrg) # Clean down - delete the org we created r = requests.delete(url+'org/'+changeOrg['id'],headers={'Authorization':token}) if r.status_code <> 200 : raise Exception(str(r.text)) print "Successfully deleted Org %(name)s (%(id)s)" % changeOrg ``` #### Create and delete an organization: ```python ''' Sample Python Code for Xponent Journey Hub API - Create and delete organization ''' # Python Sample Code to create orgs etc from os import environ import hashlib import uuid import json import requests # Check that we have the correct environment variables first before we do anything else if environ.get('KW_HUB') is None or environ.get('KW_USERID') is None or environ.get('KW_SECRET') is None: print('The KW_HUB, KW_USERID and KW_SECRET environment variables need to be set for this script') exit(1) # Hub Fully qualified hostname myHub = environ.get('KW_HUB') # Your user Id - as a string myId = environ.get('KW_USERID') # Your API secret here - from Account Settings page for this user mySecret = environ.get('KW_SECRET') # A nonce used to hash the secret nonce = str(uuid.uuid1()) def hashSecret(secret, nonce): return hashlib.sha256((secret+nonce).encode('utf-8')).hexdigest() # header object for requests headers = { # authorization will be set after an authorization token is generated 'authorization': None, 'content-type': 'application/json' } credentials = { # user id must be passed in as a string 'userId': myId, # time based uuid id as nonce 'nonce': nonce, 'hash': hashSecret(mySecret, nonce) } # url for creating authorization token - replace with your end point url = 'https://{}/api/v1/'.format(myHub) # Request the token using the calcculate credentials r = requests.post( url+'token', headers={'Content-Type': 'application/json'}, json=credentials) if r.status_code != 200: raise Exception(str(r.text)) # Now we should have a token token = dict(r.json())['tokens'][0]['token'] #print '\nSuccessfully got token: '+token+'\n' # Create a new Org r = requests.post(url+'org', headers={'Authorization': token}, json={"name": "neworg-"+str(uuid.uuid1())[:5], "parentId": '1'}) if r.status_code != 200: raise Exception(str(r.text)) newOrg = dict(r.json())['organizations'][0] print(f'Successfully created new Org {newOrg["name"]} {newOrg["id"]}') # Change the name of the org r = requests.put(url+'org/'+newOrg['id'], headers={'Authorization': token}, json={"name": "changed-"+str(uuid.uuid1())[:5]}) if r.status_code != 200: raise Exception(str(r.text)) changeOrg = dict(r.json())['organizations'][0] print(f'Successfully changed org name {newOrg["name"]} to {changeOrg["name"]} ({changeOrg["id"]})') # Clean down - delete the org we created r = requests.delete( url+'org/'+changeOrg['id'], headers={'Authorization': token}) if r.status_code != 200: raise Exception(str(r.text)) print(f'Successfully deleted Org {changeOrg["name"]} ({changeOrg["id"]})') ```