Workbench API BETA (2.13.60)

Introduction

URL: https://workbench.expel.io/api/v2

The Workbench Public API gives organizations the means to access and manipulate their Expel Workbench data using custom API clients. Want to extract information into your reporting system? Use this API! Have some innovative data science idea for analyzing Expel alert evidence? Use this API! Want to write your own custom browser plugin to make your team's analysts more efficient? Use this API!


The majority of the Workbench API implements the jsonapi spec.

jsonapi-server Post Processing

Only the routes that do not follow the JSON:API spec have been defined in the 'CUSTOM ROUTES' section below.

Authentication

User authentication

User authentication is accomplished using the /login API route. This route is used to pass user authentication credentials to the API. If authentication is successful, the /login returns a temporary (default: 13 hours) Bearer Token (access_token) that can be used to access the API.

The request body requires a flat json document with three properties: username, password, and an otp.

    curl 'https://workbench.expel.io/api/v2/login' \
        -X POST \
        -H 'Content-Type: application/json' \
        --data-binary '{ "username": "sample_user@expel.io", "password": "secret", "otp": "123456" }'
    {
      "access_token": "<access_token>",
      "created_at": 1532537770,
      "expires_in": 46800,
      "token_type": "bearer",
      "user_id": "0796cba8-3c96-4984-8d51-22222381f870",
      "username": "sample_user@expel.io",
      "role": "expel_admin",
      "organization_id": "2ca75135-5a84-4298-b8ba-b5e0fd6fb576",
      "realm": "public"
    }

Note: as an alternative to the otp property, you can pass an X-ExpelInc-Otp http request header with the otp token. This is useful when interfacing with single login frameworks

The otp property or X-ExpelInc-Otp header must be populated with the correct 6 digit two-factor authentication code (e.g. Google Auth code) for authentication to succeed. If the Workbench API is not given an OTP token, authentication will fail with a 401 with the response header X-ExpelInc-Otp set to required.

After authentication, the rest of the API may be accessed using the Bearer Token returned in the access_token field. Pass that token back to the API in the Authorization header using Bearer as the credential type.

    curl 'https://workbench.expel.io/api/v2/expel_alerts' \
        -H 'Authorization: Bearer <access_token>'

Browser authentication

Browser authentication uses the same user authentication mechanism described in the previous sections of this document.
However, if you wish for the Workbench API to set a browser cookie in addition to a Bearer Token, you can pass the
optional X-Accepts-Auth-Cookie http request header set to true.

    curl 'https://workbench.expel.io/api/v2/login' \    
        -D - -o /dev/null \    
        -X POST \    
        -H 'Content-Type: application/json' \    
        -H 'X-Accepts-Auth-Cookie: true' \    
        --data-binary '{ "username": "sample_user@expel.io", "password": "secret", "otp": "123456" }'    
    . . .    
    Set-Cookie: token=<access_token>; Path=/; Expires=Thu, 26 Jul 2018 06:05:41 GMT; HttpOnly; Secure    
    . . .    

That cookie will then be used to authenticate all future API calls in a browser. The cookie will expire in tandem with
the access_token.

API key authentication

API keys are obtained through your Expel Engagement Manager. The API key amounts to a Bearer access_token without an expiration that is tied to a specific API client (as opposed to a specific user). This is particularly useful for daemon services, automated scripts, ETL jobs, etc.
API key Bearer tokens are used in the same manner as the temporary user Bearer tokens as described in the previous sections of this document.

    curl 'https://workbench.expel.io/api/v2/expel_alerts' \
        -H 'Authorization: Bearer <api_key>'

CRUD operations

GET /:resource

See jsonapi spec for more details of request and response formatting of this request.

Use this route to query or list records of a particular resource type (e.g. investigations, expel_alerts, security_devices, etc). All common query parameters may be used on this route. See Common Query Parameters for information on the available query parameters.

    # querying the expel_alerts resource
    curl 'https://workbench.expel.io/api/v2/expel_alerts` \
        -H 'Authorization: Bearer <access_token>'

GET /:resource/:id

See jsonapi spec for more details of request and response formatting of this request.

Use this route to retrieve a specific document. The include query parameter may be used on this route.

    # Get a specific investigation, and include its related expel_alerts in the response.
    curl 'https://workbench.expel.io/api/v2/investigations/10e7bfac-27ff-4381-a0ec-5c479b73342b?include=expel_alerts' \
        -H 'Authorization: Bearer <access_token>'

POST /:resource

See jsonapi spec for more details of request and response formatting of this request.

Use this route to create a new document of a particular resource type.

    # Create a new user account
    curl 'https://workbench.expel.io/api/v2/user_accounts' \
        -X POST \
        -H 'Authorization: Bearer <access_token>' \
        -H 'Content-Type: application/json' \
        --data-binary '{
            "data": {
                "type": "user_accounts",
                "attributes": {
                    "display_name": "Sample User",
                    "email": "sample_user@expel.io",
                    "role": "organization_analyst"
                }
            }
        }'

PATCH /:resource/:id

See jsonapi spec for more details of request and response formatting of this request.

Use this route to update fields and/or overwrite relationships on a specific document. The include query parameter may be used on this route to include related resources in the response.

    # Change the title of an investigation AND set (overwrite!) the expel_alerts relationship
    curl 'https://workbench.expel.io/api/v2/investigations/155901f0-d5b1-4158-a1a1-85370c6db07f' \
        -X PATCH
        -H 'Authorization: Bearer <access_token>' \
        -H 'Content-Type: application/json' \
        --data-binary '{
            "data": {
                "type": "investigations",
                "attributes": {
                    "title": "Sample Investigation"
                },
                "relationships": {
                    "expel_alerts": {
                        "data": [
                            { "id": "d3d4edaa-8acc-40f5-bc0a-5d40c910cac0", "type": "expel_alerts" },
                            { "id": "80f72c0f-6f03-421c-8436-61fc9ca6c356", "type": "expel_alerts" }
                        ]
                    }
                }
            }
        }'  

DELETE /:resource/:id

See jsonapi spec for more details of request and response formatting of this request.

Use this route to delete a specific document.

    # Delete a timeline entry 
    curl 'https://workbench.expel.io/api/v2/timeline_entries/ddd488d8-d0e4-4cbe-bfba-aa3e75a9d103' \
        -X DELETE
        -H 'Authorization: Bearer <access_token>'

Relationships

GET /:resource/:id/:relation

See jsonapi spec for more details of request and response formatting of this request.

Use this route to query or list records related to a particular known record. All common query parameters may be used on this route. See Common Query Parameters for information on the available query parameters.

    # querying all expel_alerts related to investigation with the id of 6e8def26-a645-49b6-9688-ed2fd5385726
    curl 'https://workbench.expel.io/api/v2/investigations/6e8def26-a645-49b6-9688-ed2fd5385726/expel_alerts` \
        -H 'Authorization: Bearer <access_token>'

This route is used to query actual related resource records and only offers a GET method. It's behavior is similar to the GET /:resource route. If you are looking to get information about (or modify) the actual relationship links between records, see the various GET|POST|PATCH|DELETE /:resource/:id/relationship/:relationship routes.

GET /:resource/:id/relationships/:relation

See jsonapi spec for more details of request and response formatting of this request.

Use this route to return the meta information about a specific relationship. This route does NOT return the actual related records (see GET /:resource/:id/:relation for that). You can use the ?include=<relation> query parameter to get the records returned in this call, but this is not recommended as the include query parameter is resource intensive, is not paginated, and does not support filtering/sorting. Instead, use this route to discover links and meta information about the relationship.

    # querying information about the vendor_alerts relationship of a specific expel_alert
    curl 'https://workbench.expel.io/api/v2/expel_alerts/27340b1e-6f3f-43f1-9ba5-d7c1d9d0dcae/relationships/vendor_alerts` \
        -H 'Authorization: Bearer <access_token>'

POST /:resource/:id/relationships/:relation

See jsonapi spec for more details of request and response formatting of this request.

Use this route to append new records to an existing relationship on a specific resource.


    # see what expel_alerts are related to a specific investigation
    curl 'http://workbench.expel.io/api/v2/investigations/70f5c8c9-5b7f-4295-a1e6-0e6e4dfa8c6e/expel_alerts?fields%5Bexpel_alerts%5D=id' \
        -H 'Authorization: Bearer <access_token>' \
        | jq '.data[] | .id'
    "a61863e5-e15f-4586-9612-a40bf7e2ab64"
    "20bce40b-654a-4830-91a2-701524b0668a"

    # Append two new expel_alerts to an investigation
    curl 'https://workbench.expel.io/api/v2/investigations/70f5c8c9-5b7f-4295-a1e6-0e6e4dfa8c6e/relationships/expel_alerts' \
        -X POST \
        -H 'Authorization: Bearer <access_token>' \
        -H 'Content-Type: application/json' \
        --data-binary '{
            "data": [
                { "type": "expel_alerts", "id": "6ff0cc08-23b2-4afd-9e98-6aa072c621d5" },
                { "type": "expel_alerts", "id": "6084b93c-506e-4594-87e9-1c0198045b55" }
            ]
        }'

    # check to see if the relationships were appended.  Note that the expel_alerts that were already related to this 
    # investigation are still there.
    curl 'http://workbench.expel.io/api/v2/investigations/70f5c8c9-5b7f-4295-a1e6-0e6e4dfa8c6e/expel_alerts?fields%5Bexpel_alerts%5D=id' \
        -H 'Authorization: Bearer <access_token>' \
        | jq '.data[] | .id'
    "a61863e5-e15f-4586-9612-a40bf7e2ab64"
    "20bce40b-654a-4830-91a2-701524b0668a"
    "6ff0cc08-23b2-4afd-9e98-6aa072c621d5"
    "6084b93c-506e-4594-87e9-1c0198045b55"

PATCH /:resource/:id/relationships/:relation

See jsonapi spec for more details of request and response formatting of this request.

Use this route to overwrite a relationship of a specific resource.


    # see what expel_alerts are related to a specific investigation
    curl 'http://workbench.expel.io/api/v2/investigations/70f5c8c9-5b7f-4295-a1e6-0e6e4dfa8c6e/expel_alerts?fields%5Bexpel_alerts%5D=id' \
        -H 'Authorization: Bearer <access_token>' \
        | jq '.data[] | .id'
    "a61863e5-e15f-4586-9612-a40bf7e2ab64"
    "20bce40b-654a-4830-91a2-701524b0668a"

    # Overwrite the expel_alerts related to this investigation
    curl 'https://workbench.expel.io/api/v2/investigations/70f5c8c9-5b7f-4295-a1e6-0e6e4dfa8c6e/relationships/expel_alerts' \
        -X PATCH \
        -H 'Authorization: Bearer <access_token>' \
        -H 'Content-Type: application/json' \
        --data-binary '{
            "data": [
                { "type": "expel_alerts", "id": "6ff0cc08-23b2-4afd-9e98-6aa072c621d5" },
                { "type": "expel_alerts", "id": "6084b93c-506e-4594-87e9-1c0198045b55" }
            ]
        }'

    # check to see if the relationships were appended.  Note that the expel_alerts that were already related to this 
    # investigation have been overwritten!
    curl 'http://workbench.expel.io/api/v2/investigations/70f5c8c9-5b7f-4295-a1e6-0e6e4dfa8c6e/expel_alerts?fields%5Bexpel_alerts%5D=id' \
        -H 'Authorization: Bearer <access_token>' \
        | jq '.data[] | .id'
    "6ff0cc08-23b2-4afd-9e98-6aa072c621d5"
    "6084b93c-506e-4594-87e9-1c0198045b55"

This route can also be used to set parent relationships. Note that when setting a parent relationship like this, "data" is NOT an array, since there can only ever be a single value.

    # Establish a parent security_device for an existing security_device (self-referencing m:1 relationship)

    curl 'https://workbench.expel.io/api/v2/security_devices/4819bb1e-bad4-4b9b-a154-7a9aac9654a2/relationships/parent_security_device' \
        -X PATCH \
        -H 'Authorization: Bearer <access_token>' \
        -H 'Content-Type: application/json' \
        --data-binary '{
            "data": { "type": "security_device", "id": "1dd4ee04-792a-4052-9968-66cf347fe1b7" }
        }'

DELETE /:resource/:id/relationships/:relation

See jsonapi spec for more details of request and response formatting of this request.

Use this route to unlink specific records in a relationship. Note that unlike traditional HTTP DELETE calls, this call requires a body. Only the records listed in the body will be removed from the relationship.

Tip: If you want to remove all records from the relationship, use PATCH /:resource/:id/relationships/:relation with an empty array!

    # see what expel_alerts are related to a specific investigation
    curl 'http://workbench.expel.io/api/v2/investigations/70f5c8c9-5b7f-4295-a1e6-0e6e4dfa8c6e/expel_alerts?fields%5Bexpel_alerts%5D=id' \
        -H 'Authorization: Bearer <access_token>' \
        | jq '.data[] | .id'
    "a61863e5-e15f-4586-9612-a40bf7e2ab64"
    "20bce40b-654a-4830-91a2-701524b0668a"
    "6ff0cc08-23b2-4afd-9e98-6aa072c621d5"
    "6084b93c-506e-4594-87e9-1c0198045b55"

    # Remove two expel_alerts from this investigation
    curl 'https://workbench.expel.io/api/v2/investigations/70f5c8c9-5b7f-4295-a1e6-0e6e4dfa8c6e/relationships/expel_alerts' \
        -X DELETE \
        -H 'Authorization: Bearer <access_token>' \
        -H 'Content-Type: application/json' \
        --data-binary '{
            "data": [
                { "type": "expel_alerts", "id": "6ff0cc08-23b2-4afd-9e98-6aa072c621d5" },
                { "type": "expel_alerts", "id": "6084b93c-506e-4594-87e9-1c0198045b55" }
            ]
        }'

    # check to see if the relationships were appended.  Note that the expel_alerts that were already related to this 
    # investigation are still there.
    curl 'http://workbench.expel.io/api/v2/investigations/70f5c8c9-5b7f-4295-a1e6-0e6e4dfa8c6e/expel_alerts?fields%5Bexpel_alerts%5D=id' \
        -H 'Authorization: Bearer <access_token>' \
        | jq '.data[] | .id'
    "a61863e5-e15f-4586-9612-a40bf7e2ab64"
    "20bce40b-654a-4830-91a2-701524b0668a"

Common query parameters

Includes

Syntax: ?include=<relation>[,<relation>[...]]

The include query parameter allows you to specify which relationship records you want included in the response. This is useful when you are querying for a record, and want to resolve specific relationship data without making multiple calls.

    # request a specific investigation, and include that investigation's organization and create/update actors in the response.
    curl 'http://workbench.expel.io/api/v2/investigations/aa8492a2-b1f2-4ccb-a4dd-ff76d4903ee9?include=organization,created_by,updated_by' \
        -H 'Authorization: Bearer <access_token>'

Using the include query parameter will have the following effects on the API's response:

  1. The data element of the included 1:m ("has many") relationships will be populated with the array of id's
     {
         "data": {
             "attributes": {},
             "relationships": {
                 "<relation>": {
                     "data": [
                         { "type": "<resource>", "id": "<uuid>" },
                         { "type": "<resource>", "id": "<uuid>" }
                     ]
                 }
             }
         },
         "included": [
         ]
     }
  2. The included array at the top level of the response will include the records references in the relationships sections across all returned records.
         {
             "data": {
             },
             "included": [
                 {
                     "type": "<resource>",
                     "id": "<uuid>",
                     "attributes": {}