Xurrent GraphQL API
The Xurrent GraphQL API allows to create precise and flexible queries for the data you need to integrate with Xurrent, 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 Xurrent 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 Xurrent 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 Xurrent 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 Xurrent, or by creating an OAuth Application from the Settings console in Xurrent.
Each request to the Xurrent GraphQL API must supply two HTTP headers:
Authorization: Bearer <oauth-token>
X-4me-Account: <accountID>
The Xurrent 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 Xurrent 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.
-
Query type — defines how GraphQL operations reads data. It is analogous to performing HTTP
GET
, but sent usingPOST
. -
Mutation type — defines how GraphQL operations change data. It is analogous to performing HTTP methods such as
POST
,PATCH
, andDELETE
. The various mutation types supported in the GraphQL API are all sent usingPOST
. The other HTTP methods are not used.
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:
- Replace the static value in the query with
$variableName
- Declare
$variableName
as one of the variables accepted by the query - Include an extra parameter
variables
in the call sent to the GraphQL API. This parameter will contain a JSON dictionary ofvariableName: value
pairs.
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 Xurrent GraphQL API is introspective. This means you can ask the Xurrent 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 Xurrent REST API request it is the nodeID
field.
In Xurrent Webhook payloads they are the fields that end with _nodeID
in their name.
A node ID in Xurrent 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 Xurrent GraphQL API resources and operations. These quotas are in place to protect against excessive calls.
The Xurrent 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:
- Clients must supply a
first
orlast
argument on any connection - Values of
first
andlast
must be within 1-100. - The maximum query depth is 13 levels.
- Individual calls cannot request more than 500,000 total nodes.
For example, given this query:
{
requests(first: 100) {
totalCount
nodes {
requestId
configurationItems(first: 10) {
nodes {
id
name
}
}
}
}
}
Then the requested total nodes is:
- 100 requests +
- 100 x 10 configuration items
= 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:
- 100 requests +
- 100 x 10 configuration items +
- 100 x 10 x 5 CI relations +
- 100 x 20 affected SLAs
= 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:
- A maximum of 20 requests per 2 seconds window
- A maximum of 3,600 requests per 60 minutes window
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.xurrent.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 Xurrent GraphQL API imposes a limit of sending a maximum of only 1 mutation per request. Please refer to the Xurrent 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 Xurrent'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.xurrent.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:
- Add up the number of requests needed to fulfill each unique connection in the call. Assume every request will reach the
first
orlast
argument limits. - Divide the number by 100 and round the result to get the final cost. This step normalizes large numbers.
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 to query the rate limit node
- 1 to query 100 request nodes
- 100 to query the configuration items and affected SLAs
- 100 x 15 to query the CI relation nodes
= 1,602
Dividing by 100 and rounding gives a final query cost of: 16