4me GraphQL API

The 4me GraphQL API allows to create precise and flexible queries for the data you need to integrate with 4me, getting predictable results.

For a general introduction to the GraphQL query language please see https://graphql.org.

We provide a client library to simplify usage of this API using .Net, available as a NuGet package.

Service URL

The 4me GraphQL API requires a point of entry service URL that references the instance of a specific environment and region:

Instance Environment Region
https://graphql.4me.com Production Global
https://graphql.au.4me.com Production Australia
https://graphql.uk.4me.com Production United Kingdom
https://graphql.ch.4me.com Production Switzerland
https://graphql.us.4me.com Production United States
https://graphql.4me.qa Quality Assurance Global
https://graphql.au.4me.qa Quality Assurance Australia
https://graphql.uk.4me.qa Quality Assurance United Kingdom
https://graphql.ch.4me.qa Quality Assurance Switzerland
https://graphql.us.4me.qa Quality Assurance United States
https://graphql.4me-demo.com Demo Global

The endpoint remains constant no matter what operation you perform.

All query and mutation requests to the 4me GraphQL API must use the HTTP POST method. The request body should specify exactly which operation is to be used.

Please note the use of https:// in the URL above. All communication is encrypted over HTTPS. Non-secure requests are rejected, so we recommend establishing a test connection with the secure API entry point before sending sensitive data.

Authentication

The 4me GraphQL API requires an OAuth token for making authenticated requests.

You can obtain an OAuth token either by generating a Personal Access Token from My Profile in 4me, or by creating an OAuth Application from the Settings console in 4me.

Each request to the 4me GraphQL API must supply two HTTP headers:

The 4me account ID value passed in the X-4me-Account header determines the current account that the API request will use. The view current_account, which is the default view for most root-connection queries, limits results to records in the given current account.

A personal access token can be generated in the 4me Specialists console by going to 'My Profile' and then selecting the 'Personal Access Tokens' section from the menu. Make sure to add sufficient permissions to the token by adding the scopes required by your API requests. To perform queries the 'Read' action should be allowed on the model matching the node types of the collections accessed. Mutations require either the 'Create' or 'Update' action for their model.

Example request:

    $ curl -H "Authorization: Bearer cgbb2rhPCMHSE...SQxXkjYBzNOPzx" \
           -H "X-4me-Account: widget" \
           -H "Content-Type: application/json" \
           -X POST \
           -d "{\"query\": \"{ me { id name } }\"}" \
           https://graphql.4me.com/

Response:

    {"data":{"me":{"id":"NG1lLmNvbS9QZXJzb24vNg","name":"Howard Tanner"}}}

Note that for this example to work your OAuth token must have added a scope to allow action Me - Name or Me - Profile or me - All.

Root Types

The functionality of a GraphQL API is described by its schema. Every GraphQL schema has a root type for both queries and mutations.

Variables

GraphQL queries and mutations often require input arguments (e.g. the email address to look up a person or the value to set for a configuration item's label). These arguments can be included directly in the query string (the examples on this site tend to use this approach), but this is not the recommended approach. GraphQL has first-class support for variables to prevent clients having to do manipulation of the query string. Besides preventing security issues arising from including user supplied values in queries, using these variables also facilitates logging without including personal/sensitive data in log files.

When you start working with variables, you need to do three things:

A sample request could contain a query parameter using a variable emails:

query($emails: [String]!){
 people(first: 1, filter: {primaryEmail: {values: $emails}}) {
    nodes { id name primaryEmail }
  }
}

This could be accompanied by a variables parameter defining emails to be an array with howard.tanner@widget.com as its element:

{"emails": ["howard.tanner@widget.com"]}

Introspection

The 4me GraphQL API is introspective. This means you can ask the 4me GraphQL schema for details about itself. A schema defines a GraphQL API's type system, providing information about what queries and mutations it supports. Reqests from API clients are validated and executed against the schema.

For example, query __schema to list all types defined in the schema:

    query {
      __schema {
        types {
          name
          kind
          description
          fields {
            name
          }
        }
      }
    }

Query __type to get details about a single type:

    query {
      __type(name: "ServiceLevelAgreement") {
        name
        kind
        description
        fields {
          name
        }
      }
    }

It is also possible to retrieve the complete schema via a simple GET request:

$ curl -H "Authorization: bearer <oauth-token>" https://graphql.4me.com/

Note: The introspection query is the only time a GET request (and no X-4me-Account header) is accepted. When sending a request body, the request method is POST, whether it is a query or a mutation.

Global Node IDs

Node is a generic term for an object or record. You can look up a node directly, or you can access related nodes via a connection. Each node is identified by a global node ID. In GraphQL it is the id field of type ID on the node interface. In the response of a 4me REST API request it is the nodeID field. In 4me Webhook payloads they are the fields that end with _nodeID in their name.

A node ID in 4me is globally unique across systems.

Given a node ID, to find the object type in GraphQL, post for example this query:

    query {
      node(id: "NG1lLmNvbS9QZXJzb24vNg") {
        __typename
      }
    }

The response might look like:

    {
      "data": {
        "node": {
          "__typename": "Person"
        }
      }
    }

Knowing the type of the node an inline fragment can be used to query the details of the node:

    query {
      node(id: "NG1lLmNvbS9QZXJzb24vNg") {
        ... on Person {
          name
          primaryEmail
        }
      }
    }

The response might look like:

    {
      "data": {
        "node": {
          "name": "Howard Tanner",
          "primaryEmail": "howard.tanner@widget.com"
        }
      }
    }

Service Quotas

This section lists and describes the quotas that apply to 4me GraphQL API resources and operations. These quotas are in place to protect against excessive calls.

The 4me GraphQL API has the following service quotas in place:

Total Nodes Limit

To pass schema validation, all GraphQL API calls must meet the following requirements:

For example, given this query:

    {
      requests(first: 100) {
        totalCount
        nodes {
          requestId
          configurationItems(first: 10) {
            nodes {
              id
              name
            }
          }
        }
      }
    }

Then the requested total nodes is:

= 1,100 total nodes

For example, given this query:

    {
      requests(first: 100) {
        totalCount
        nodes {
          requestId
          configurationItems(first: 10) {
            nodes {
              id
              name
              ciRelations(first: 5) {
                nodes {
                  relationType
                  configurationItem {
                    id
                    name
                  }
                }
              }
            }
          }
          affectedSlas(first: 20) {
            edges {
              cursor
              node {
                supplier {
                  name
                }
              }
            }
          }
        }
      }
    }

Then the requested total nodes is:

= 8,100 total nodes

Rate Limits

Clients should expect to exceed rate limiting conditions, and respond to these conditions properly.

Rate limits are keyed by the authenticated user when a personal access token is used. Rate limits are keyed by the combination OAuth application and authenticated user when an application OAuth token is used.

Request Rate Limits

The following request rate limits are in place:


Note The precise values of these request rate limits may change at any time. Do not hard-code these values in your API client code.

When a rate limit window has been exceeded an error response is returned and the HTTP header Retry-After indicates how long to wait (in seconds) before making a new request. The value is also available via the retryAfter value found within the response body's JSON object.

An example response might look like:

Status: 429 Too Many Requests
Content-Type: application/json; charset=utf-8
Retry-After: 30

{
  "documentationUrl":"https://developer.4me.com/graphql/#service-quotas",
  "message": "Too Many Requests",
  "retryAfter": 30
}

This response instructs the API client to wait 30 seconds before attempting to send a new request.

By programmatically evaluating the Retry-After HTTP header or the retryAfter value from the response body an API client can wait for the indicated number of seconds before retrying the same request.

Mutation Cost Limit

Currently the 4me GraphQL API imposes a limit of sending a maximum of only 1 mutation per request. Please refer to the 4me Bulk API if a large number of mutations must be made.

Query Cost Limit

To accurately represent the cost of a query, the GraphQL API calculates a query cost score based on a normalized scale of points. A query's score factors in the first and last arguments on all connections. This is done to determine the potential load on 4me's systems. The GraphQL API query cost rate limit is 5,000 points per hour.

Note The current formula and query cost limit are subject to change as we observe how the GraphQL API is used.

Returning the Query Cost Rate Limit Status

The query cost rate limit status can be queried as part of the GraphQL query by querying fields on the rateLimit object.

Example request:

{
  me {
    name
    supportID
  }
  rateLimit {
    limit
    cost
    remaining
    resetAt
  }
}

Example response:

{
  "data": {
    "me": {
      "name": "Howard Tanner",
      "supportID": "430134"
    },
    "rateLimit": {
      "limit": 5000,
      "cost": 1,
      "remaining": 4999,
      "resetAt": 1600672683
    }
  }
}

The query cost is also included in the headers of every GraphQL request. The following headers are included:

X-CostLimit-Limit

The maximum number of points the client is permitted to consume in a 60-minutes window.

X-CostLimit-Cost

The point cost for the current call that counts against the query cost rate limit.

X-CostLimit-Remaining

The number of points remaining in the current query cost rate limit window.

X-CostLimit-Reset

The time at which the current query cost rate limit window resets in UTC epoch seconds.

When a request is made that would exceed the number of points left, then an example response might look like:

Status: 200 OK
Content-Type: application/json; charset=utf-8

{
  "errors": {
    "message": "Insufficient query cost points remaining.",
    "documentationUrl": "https://developer.4me.com/graphql/#service-quotas",
    "rateLimit": {
      "limit": 5000,
      "cost": 1,
      "remaining": 0,
      "resetAt": 1600672683
    }
  }
}
Query Cost Calculation

Querying the rateLimit object returns the query cost score. To calculate the cost of a query before sending the query the following calculation can be followed:


Note The minimum cost of a request to the GraphQL API is 1, representing a single request.

For example, given this query:

    {
      rateLimit {
        cost
      }
      requests(first: 100) {
        totalCount
        nodes {
          requestId
          configurationItems(first: 15) {
            nodes {
              id
              name
              ciRelations(first: 5) {
                nodes {
                  relationType
                  configurationItem {
                    id
                    name
                  }
                }
              }
            }
          }
          affectedSlas(first: 20) {
            edges {
              cursor
              node {
                supplier {
                  name
                }
              }
            }
          }
        }
      }
    }

Then the query cost is:

= 1,602

Dividing by 100 and rounding gives a final query cost of: 16