Skip to main content

Journyx API tutorial

This tutorial will guide you through the process of making your first API request to the Journyx API. You will need a Journyx account with API access enabled, and an API key to authenticate your requests.

If you haven't already, be sure to read the REST interaction overview section.

Back to the documentation main page.

REST API interaction summary

As discussed in the REST overview section, each resource or object in the system has a unique URL in the API. You interact with these objects by sending HTTP requests to the server; for example, the GET method is used to retrieve a resource (or a collection of them), the POST method is used to create a resource, and DELETE is used to delete a resource. This tutorial will sometimes refer to resources and objects interchangeably.

While it's entirely possible to construct these requests on a Linux command line (with wget or curl), or a Python script, or in the JavaScript console, or with any other tool that understands HTTP, it's often easier to use a specialized HTTP request tool like Postman to make API requests for learning or experimentation purposes. Developer environments like Visual Studio Code and JetBrains PyCharm Professional also have these features built in.

Making REST API requests in VS Code

This section provides a tutorial on how to make API requests directly in Visual Studio Code (VS Code) without writing a full client program. Hopefully, this introduction to directly making API calls will help inform your future programming and make writing client code easier.

Using VS Code is not required to make API requests or to follow along with this tutorial. There are other tools like Postman or PyCharm Professional that can be used to make direct API requests as well. We are using VS Code in this tutorial because it is a popular IDE and it makes it easy to directly construct HTTP requests. Experienced developers can use any preferred tools and programming languages to interact with the API as well, because it's HTTP based and works with any programming language.

In order to make API requests, you'll need at least two basic pieces of information:

Admin-level account

For reasons that will become apparent, we will largely assume in this tutorial that you are using an Admin-level account for purposes of management, control, and data import/export. In general, a "regular time entry user" account will only have access to API features needed for time entry purposes.

For instance, assume we have our Journyx server instance https://example.apps.journyx.com, and we want to make API requests to this server. Obviously, we can write a Python (or JavaScript, or C#, etc.) program that makes API calls. But it is nice to have a more straightforward way to simply make calls directly and observe responses. This can also help with the initial development of a new API as you can begin testing calls against it immediately, or even before you begin writing it. You can sketch out the interactions and data formats before you write a single line of code. Using tools like VS Code's REST Client Extension , you can then save these requests to review or replay later.

Using the REST Client extension

To get started in Visual Studio Code, first install the REST Client extension then create a new file with a .http extension. This will allow you to write HTTP requests directly in the file and run them to see the responses.

REST Client extension

This extension provides a simple format where you can type out structured HTTP requests and execute them to see the results. Give the file a name like "API Experiments.http" and start editing the file.

Multiple requests

It's important to separate multiple requests with a line that has ### as shown in the screenshot below. This is how the REST Client extension knows where one request ends and the next one begins.

To make your first API request, first make sure you have the extension installed and an ".http" file open. Then, in the HTTP Request file, you can try typing the following lines:

###

GET https://example.apps.journyx.com/api/v1/users
Accept: application/json
Replace the server URL

Be sure to replace example.apps.journyx.com with your own Journyx server instance URL. Please note that the screenshots provided below will show a different URL, but you should use your own server URL.

Above the GET line, you should see the text "Send Request" as shown here. If you do not see this, the extension may not be installed or you may not have the file open with a .http extension.

HTTP Requests in VS Code

Click the "Send Request" link and you should see some output appear in a new window:

First response

Authenticating requests

Notice that it says the server's response code is 401 UNAUTHORIZED and the error message mentions needing to be signed in to access that resource. At this point you may be wondering about authentication. Almost all requests in the API require you to be signed in. A notable exception would be the URL /api/v1/login, for obvious reasons. Your signed-in identity determines the resources and actions that you have access to.

401 Unauthorized responses

If you see a 401 Unauthorized response, it means you need to authenticate your request.

For the moment, let's assume you know the login and password of an Administrator account and use that. If single sign-on (SSO) is being used by the site, things become a little more complicated because you have to use an API Key, so for now let's assume the internal password database is available. (SSO vs internal can also be configured on a per-account basis.) Writing production-ready authentication code is beyond the scope of this tutorial, but is covered more in the Authentication section. An API Key can be substituted for a password in most cases, including with SSO.

There are two main ways to send an authenticated request to the Journyx API:

  • HTTP Basic Authentication - this sends the username and password every time, on every request. This is insecure if the site is not using HTTPS secure encryption. However, all Journyx hosted sites use HTTPS secure mode, which helps mitigate any security impact. However, it is still considered preferable to use the other method discussed below (wtsession cookie), if only for performance reasons. HTTP Basic Authentication is convenient for quick examples and learning or development purposes, but is not ideal for production use because it is less performant and less secure.

  • The wtsession cookie is the preferred method of authentication. Instead of sending the username and password with every request, you send a cookie that contains a secret session ID. To obtain a session cookie, send a POST request to /api/v1/login with a JSON body like:

    {
    "username": "Preston",
    "password": "1234"
    }

    This will respond with a Set-Cookie for a cookie named wtsession. Capture this and send it as a cookie header with future requests. You can also find this value in the X-Session-Id header as mentioned below, if cookies are inaccessible for some reason. The wtsession cookie should be returned to the server in future requests in a Cookie header. As we will see below, VS Code can help with this when using the REST Client extension.

  • There are other authentication methods useful to REST APIs including JSON Web Tokens, and OAuth, but those aren't currently supported in Journyx.

Basic Auth example

For purposes of this example, we'll use Basic Auth. To add basic auth to our example request, we just need to add a header line for it, but it uses an encoded format. Fortunately, the VS Code REST Client extension can handle this for us so we don't have to concern ourselves with the details of the encoding. If you're using a Python script or other client program to make these API calls, you will likely have to call a library function to handle the encoding. But in VS Code we can just put the raw username and password, then click the Send Request link to run it:

Request with Basic Auth

Basic Auth encoding

The REST Client extension handles the details of encoding the username and password for you for the Authorization header. If you're writing a program in Python or another language, you will need to encode the username and password yourself. The details of how to do that vary by language.

Click the Send Request link and you should see some output in the lower area:

Authenticated response

Now we see something a little more interesting; this looks like actual usable data. This is a collection response for the Users collection. You can see that the response is structured as a single JavaScript object with some top level keys. Here are some of the keys you will commonly see in this response object. Not all of these are shown in the picture above.

  • $count
  • $skip
  • $top
  • @nextLink
  • response_code
  • results
  • screen
  • success
  • uri

These keys are common to virtually all collection responses and are discussed in detail on the Collections and REST Overview pages.

The REST Client extension in VS Code can preserve any cookies set by the response, including the wtsession authentication cookie, so it may seem that the Basic Authentication line is only necessary on the first request, or at least until the session expires. This feature may not be turned on by default but it can be activated by turning on the rest-client.rememberCookiesForSubsequentRequests setting in the VS Code settings.

However, Basic Auth specifically makes a 1-request session that is no longer valid after the request completes, so the Set-Cookie line is actually useless when Basic Auth is used. To get a persistent web session cookie, use the /api/v1/login URL as mentioned above. VS Code will remember that cookie and allow you to use it in subsequent requests.

danger

Most of the example requests and screenshots in this tutorial do not show an authentication header, but you will always need to include one. The REST Client extension in VS Code can automatically remember and send the wtsession cookie for you if that feature is enabled. Any client programs you write may need to handle this manually.

Response headers

As shown in the screenshot above, the server also responded with several headers in addition to the main body of the response. You can see the HTTP protocol version and main response code on the top line: HTTP/1.1 200 OK. This indicated we got a valid response for this resource (URL) and can proceed accordingly. It also tells us that the content type of the response is application/json MIME type and that it's 10952 bytes long.

Error responses

It's important to check the HTTP response code for errors. A 200 OK response generally means that the request was successful, but other codes like 401 Unauthorized or 404 Not Found indicate problems. Be sure to check all responses for errors!

We also see something called an Etag that we'll come back to later; this is like a cryptographic hash of this specific version of response that can be used to detect if our local cached copy is no longer valid due to changes in the server. We will come back to the ETag later because it is necessary to make changes to an existing item.

We also see that the server added a Set-Cookie header for the wtsession authentication cookie as discussed above; we can send this back in future requests to authenticate them without using the less-secure Basic Auth header. (Note that security for session cookies still depends on using HTTPS secure encryption.) However, when Basic Auth is used, this cookie will not work for future requests. But if you get a wtsession cookie at the /api/v1/login endpoint mentioned above, you can reuse it on future requests.

There are also two headers prefixed with X-; these are custom headers that are specific to Journyx. The server can put any additional headers it wants on any given request to supply extra information for the client, typically this is "side-channel" data that is not directly incorporated into the response body.

For instance, we have X-Session-Expires, which tells us when our wtsession login cookie will expire, and we have X-Session-Id, which is another copy of the wtsession cookie, but placed outside the Set-Cookie header. This is primarily to make it easier for the mobile app to access it, because restrictions can sometimes be placed on extracting (reading) cookies.

Response data keys

As mentioned above, there are several keys that are common to our REST API responses. This helps keeps things consistent between objects. Some of the keys are only present in Collections (lists of objects) and not present on individual objects. Let's go over these response data keys in detail.

The first few response keys are common to both collections and individual objects. Some keys like error are only present when relevant. Also keep in mind that just because the server returns a JSON object doesn't mean your request worked; errors can also return structured JSON data. Always check the HTTP response code.

  • response_code - simply repeats the HTTP response code already given on the first line of the HTTP response. This can make it more convenient to access in certain scenarios. This only includes the number portion.

  • success - a boolean that indicates, in general, whether the request succeeded or an error code was generated.

  • uri - the same URL of the request reflected back for informational purposes, usually as a server-relative URL.

  • results - either an object dictionary for the main object, or else a list of these when it's a collection. Objects are discussed in more detail below. This is the meat of the response and could be considered the actual (representation of) the resource.

  • error - a dict with 2 keys representing the main or outermost error, if there was any error.

    • code - a consistent error code such as NotFoundOrNotAuthorized
    • message - the error string or message specific to the situation.
  • errors - a list (array) of error strings. May contain only 1 item, but sometimes more. Useful if multiple errors are present, as it can help the client solve multiple problems or provide additional feedback.

  • status, info, warnings - lists of additional status messages that may not always be present. These are typically presented to the user for additional feedback.

The next set of response keys are only in collections:

  • $count - the total number of items in the result set, given whatever filters are applied. Not all available results in the total set are necessarily included in the response; you may have to page through them.

  • $skip - the number of results skipped over as part of pagination. Your starting point in the slice of the total collection represented in the response. If $skip is 0 then you're on the first page of results.

  • $top - the number of results shown in the current slice in pagination. If $top plus $skip is equal to $count, then you're on the last page of results.

  • @nextLink - a link to obtain the next page of results, if any. If this is missing, you're on the last page of results.

  • screen - additional metadata for the UI screen associated with this REST API, if any. This may not always be present and is described in more detail elsewhere (TODO).

This section concludes the initial walkthrough of how to make a REST API call. The rest of this document will go into greater detail about each aspect of this, including how to create, modify, or delete objects, query a collection using a filter, and other topics.

As mentioned above, there are a few ways to authenticate, without even considering Single Sign On (SSO). For now we will assume use of the internal password database. Working with API under Single Sign On is exactly the same in almost all cases except you will need to use an API Key instead of a password.

Reviewing the standard login flow also illustrates several other required elements for making API calls; namely, the Origin or Referrer request headers, as well as X-Requested-With and the Accept and Content-Type headers.

First let us consider the logout (sign out) resource / functionality. If you make this API call while signed in, you'll get a 200 OK response, indicating you were signed in, but the logout request was successful and now your session has ended. However, if you call logout while you're not authenticated, you'll get a 401 Unauthorized response, with a message that says that you must be signed in to run this request. This illustrates how server responses and response codes are context sensitive.

Next, we'll write the /api/v1/login call. Like the logout call, you must add a few headers to make it work correctly. These headers will be explained later. Both of these calls use POST because we are creating something (a login request, or a logout request.) Arguably, to be more "REST-ful", we should be able to use the DELETE method on a session resource, but that isn't how it's currently set up.

Sign Out and Sign In requests

The server response to the Sign In request is shown here:

Sign In response

As you can see, the wtsession cookie is contained in both Set-Cookie line, which VS Code's REST Client extension will remember for the following requests, as well as in the X-Session-Id. That header, along with X-Session-Expires, were discussed above.

Accept and Content-Type headers

Also notice that we set the Accept header to say application/json. This is to tell the server that we prefer responses in the JSON format. If we leave this out, certain error conditions will return HTML or other response formats, but usually we only want to get JSON back, even when errors happen. Usually because it's simpler to deal with JSON in a client rather than HTML. Therefore it's important to tell the server with Accept: application/json that we prefer to receive JSON back. When sending data to the server in JSON format, we likewise want to tell the server explicitly that it's in that format by sending Content-Type: application/json.

Origin and X-Requested-With headers

As shown above, there are two headers that must be present in all requests (except for GET requests) - those are the Origin and X-Requested-With headers. Both of these headers are required for security reasons, to prevent cross-site request forgery (CSRF), click-jacking, and similar attacks.

The Origin and X-Requested-With headers are automatically inserted when we make API requests in the browser. However, when making standalone requests, we must include them. If the Origin header can't be set, you can also set the Referrer header instead or in addition to. Either one should be set to the main URL of the site you're trying to access, or else a sub-URL of it.

X-Requested-With: XMLHttpRequest (the entire value) must always be present.

Note: these headers are not the only mitigation against CSRF and similar attacks, but are required in order to run REST API requests on Journyx.

Up next: Collections

We've covered some of the basics of making authenticated API requests in this section. Next, we will explore one of the most important concepts in the Journyx API: collections. Collections are lists of objects that can be queried, sorted, filtered, and paginated, and are the standard way to access lists of objects in the Journyx system.