# API Access

## Overview

X-Assist allows integration with 3rd party software through its [GraphQL](https://graphql.org) API. It provides access to all features and data available in X-Assist.

The GraphQL API is served from a single endpoint:

```
https://x-assist.crisis24.com/graphql
```

## Getting started

For first time users, we recommend using the interactive query tool available at <https://x-assist.crisis24.com/devtools>. The tool offers a query editor with autocompletion and a documentation browser which allows you to explore the GraphQL schema of X-Assist.

For example, to retrieve the three most recent events, you would perform the following query:

```graphql
query {
  events(first: 3, orderBy: { field: CREATED_AT, direction: DESC }) {
    nodes {
      id
      name
      createdAt
    }
  }
}
```

The API then responds with a JSON object that looks like this:

```javascript
{
  "data": {
    "events": {
      "nodes": [
        {
          "id": "RXZlbnQtMTg2NjY5NzU=",
          "name": "Sindh: At Least Three Killed and Seven Injured in a Gas Cylinder Explosion in an Eatery in City of Karachi",
          "createdAt": "2021-04-07T09:14:01Z"
        },
        {
          "id": "RXZlbnQtMTg2NjY5NzI=",
          "name": "Shan: Reportedly 1 Person Killed and 10 Others Injured After Security Forces Open Fire on Civilians Within the Context of a Protest in Township of Nyaungshwe",
          "createdAt": "2021-04-07T09:10:07Z"
        },
        {
          "id": "RXZlbnQtMTg2NjY5NDU=",
          "name": "Gauteng: 14 Injured in a Taxi Road Traffic Accident in Location of Florida",
          "createdAt": "2021-04-07T08:43:10Z"
        }
      ]
    }
  }
}
```

## Authentication

The X-Assist API uses [OAuth 2.0 Bearer Tokens](https://oauth.net/2/bearer-tokens/) to authenticate requests.

These tokens are issued by the [X-Assist IAM](https://accounts.x-assist.crisis24.com/) module, which implements a standards-compliant OAuth 2.0 authorization server and OpenID Connect provider. You can access its configuration data at <https://accounts.x-assist.crisis24.com/.well-known/openid-configuration>.

Server-side applications should use the [Client Credentials Grant](https://oauth.net/2/grant-types/client-credentials/) to request an access token from the IAM's token endpoint.

## Request an OAuth access token from the IAM

<mark style="color:green;">`POST`</mark> `https://accounts.x-assist.crisis24.com/oauth/token`

#### Request Body

| Name           | Type   | Description                                                        |
| -------------- | ------ | ------------------------------------------------------------------ |
| client\_id     | string | The OAuth client ID of your application.                           |
| client\_secret | string | The OAuth client secret of your application.                       |
| grant\_type    | string | The OAuth grant type to use. Must be set to "client\_credentials". |

{% tabs %}
{% tab title="200 " %}

```javascript
{
    "access_token": "-sc-na0LtIghflDJ8rH8wBDdmRBSyd5iPF0znsVegeY",
    "token_type": "Bearer",
    "expires_in": 600,
    "scope": "public",
    "created_at": 1612882203
}
```

{% endtab %}
{% endtabs %}

{% hint style="warning" %}
Note that access tokens have a finite lifetime and expire at `created_at + expires_in` seconds after the Unix epoch. After this time, you must request a new token from the IAM.
{% endhint %}

{% hint style="info" %}
If you do not want to implement the token management yourself, you can use one of the existing [client libraries](https://oauth.net/code/) for your programming language/environment instead.
{% endhint %}

## Accessing the GraphQL endpoint

To query the GraphQL endpoint, the request must include an access token in the HTTP `Authorization` header (preferred) or the `access_token` query parameter.

## GraphQL query

<mark style="color:green;">`POST`</mark> `https://x-assist.crisis24.com/graphql`

#### Headers

| Name          | Type   | Description                                                                                                                  |
| ------------- | ------ | ---------------------------------------------------------------------------------------------------------------------------- |
| Authorization | string | The OAuth access token used to authenticate the request. Example value: "Bearer -sc-na0LtIghflDJ8rH8wBDdmRBSyd5iPF0znsVegeY" |

#### Request Body

| Name          | Type   | Description                                                                                                             |
| ------------- | ------ | ----------------------------------------------------------------------------------------------------------------------- |
| query         | string | The GraphQL operation to execute. Can be a query, mutation or a subscription. See <https://graphql.org/learn/queries/>. |
| operationName | string | Only required if multiple operations are present in the query. See <https://graphql.org/learn/queries/#operation-name>. |
| variables     | object | A dictionary of dynamic query arguments. See <https://graphql.org/learn/queries/#variables>.                            |

{% tabs %}
{% tab title="200 The GraphQL query was executed, but this does not imply that it was successful. See the discussion below." %}

```javascript
{
  "data": { ... },
  "errors": [ ... ],
  "extensions": { ... }
}
```

{% endtab %}

{% tab title="401 If your access token is missing, invalid or expired, the GraphQL endpoint will return an error:" %}

```javascript
{
  "error": "Unauthorized: Access token is not valid or missing."
}
```

{% endtab %}
{% endtabs %}

{% hint style="info" %}
GraphQL does not use HTTP status codes, but instead uses an `errors` array in the response.

The reason for this is that complex queries (e.g. ones that use multiplexing or are only partially executed) cannot be reasonably mapped to a single status code.

Therefore, API clients must always check the `errors` array in the response. In addition to that, our GraphQL mutation payloads have a separate array of `UserError` objects.

* Entries in the top-level `errors` array are developer-facing errors (e.g. for syntax errors in the GraphQL query).
* Mutation errors are used to communicate user-facing errors, e.g. when an input field exceeds its maximum length.
  {% endhint %}

Here is a full example showing how to retrieve the three most recent events from X-Assist:

{% tabs %}
{% tab title="HTTP Request" %}

```http
POST https://x-assist.crisis24.com/graphql
Authorization: Bearer -sc-na0LtIghflDJ8rH8wBDdmRBSyd5iPF0znsVegeY
Content-Type: application/json

{
    "query": "query { events(first: 3, orderBy: { field: UPDATED_AT, direction: DESC }) { nodes { id name createdAt }}}",
    "variables": null,
    "operationName": null
}
```

{% endtab %}

{% tab title="HTTP Response" %}

```http
HTTP/2 200
Content-Type: application/json; charset=utf-8
Cache-Control: max-age=0, private, must-revalidate

{
  "data": {
    "events": {
      "nodes": [
        {
          "id": "RXZlbnQtMTg2NjcwMzc=",
          "name": "KwaZulu-Natal: A Student Protest Takes Place in City of Durban",
          "createdAt": "2021-04-07T10:00:53Z"
        },
        {
          "id": "RXZlbnQtMTg2NjY5NzY=",
          "name": "Wisconsin: Two Killed and two Injured in a Shootout at a Petrol Station in City of Milwaukee",
          "createdAt": "2021-04-07T09:15:09Z"
        },
        {
          "id": "RXZlbnQtMTg2NjY5NzU=",
          "name": "Sindh: At Least Three Killed and Seven Injured in a Gas Cylinder Explosion in an Eatery in City of Karachi",
          "createdAt": "2021-04-07T09:14:01Z"
        }
      ]
    }
  }
}
```

{% endtab %}
{% endtabs %}

## Date and time

Timestamps and other time-related objects are crucial concepts in the X-Assist API, and it is critical that API clients handle them in the right way.

#### Timestamps

Fields with the scalar type `DateTime` are the most basic ones. A `DateTime` value is an [ISO 8601 formatted string](https://en.wikipedia.org/wiki/ISO_8601#Combined_date_and_time_representations) that represents an instant on the global timeline. `DateTime` values in API responses are usually in UTC (indicated by a trailing `Z`), but API clients must be able to handle values with other offsets as well. Unqualified `DateTime` inputs (with an unspecified offset) are assumed to be in UTC.

A `DateTime` field on an object is frequently accompanied by a `timeZone` field that contains an [IANA Time Zone ID](https://www.iana.org/time-zones) that API clients can use to convert the UTC value back into local time. The time zone field can be on the same object as the timestamp itself (as is the case with `HotelSegment` objects, for example), or within a nested object (e.g. `FlightLeg` objects do not have `timeZone` fields, but the associated airports do).

#### Time intervals

`DateRange` objects combine two `DateTime` fields to represent the time interval between the two points in time. The lower bound of the date range is always inclusive, while the upper bound is always exclusive.

#### Durations

Durations that do not refer to a fixed point in time are represented either as integers or as values of the type `Duration`. For integers, the value is given in seconds. `Duration` fields use [ISO 8601 formatting](https://en.wikipedia.org/wiki/ISO_8601#Durations).

ISO 8601 formatted durations can also be used in the `DateTimeOrDuration` scalar, which is commonly used in the `DateFilter` type. Here, durations are interpreted relative to the current time and negative values are permitted. For example, a date filter that covers the last 24 hours looks like this:

```graphql
{
  gte: "-PT24H",
  lt: "P0D"
}
```

## Geographical data

The X-Assist GraphQL schema uses the following types to represent geographical data:

* `GeoPoint` / `GeoPointInput`
* `GeoJSON`

These types always use [WGS 84](https://en.wikipedia.org/wiki/World_Geodetic_System#1984_version) / [SRID 4326](https://spatialreference.org/ref/epsg/wgs-84/) as spatial reference system.

## Internationalization (I18n)

The content of X-Assist is available in the following languages:

* Chinese
* English
* French
* German
* Italian
* Japanese
* Portuguese
* Spanish

The HTTP `Accept-Language` header determines the language in which the content of an API response is returned. Additionally, some fields in the GraphQL schema support a `language` argument that allows API clients to explicitly request content in a specific language.

For example, clients may wish to retrieve the original English headline of an event in addition to machine translated headline:

```graphql
query {
  event(id: "RXZlbnQtMTk5NTYxOTU=") {
    name # Language is auto-selected by X-Assist (German in this example)
    originalName: name(language: EN) # Explicitly request the original English text
    latinName: name(language: LA) # X-Assist does not support Latin yet, so this will return null
  }
}
```

```json
{
  "data": {
    "event": {
      "name": "Zhytomyr: Mindestens fünf getötete Zivilisten bei Luftangriff auf die Stadt Malyn",
      "originalName": "Zhytomyr: At Least Five Civilians Killed in Air Strike on the City of Malyn",
      "latinName": null
    }
  }
}
```

## Pagination

Collections of objects are exposed in the X-Assist API in two different ways. Collections with only a few items are exposed as plain lists (i.e. arrays) for simplicity:

```graphql
type Event {
  # Each event typically only involves a few militant groups (if any).
  militantGroups: [String!]!
}
```

Large collections use [Relay Connections](https://relay.dev/graphql/connections.htm), a cursor-based pagination mechanism and the de-facto standard used by many GraphQL APIs.

Let's re-visit the earlier example query that retrieves the three most recent events:

```graphql
query {
  events(first: 3, orderBy: { field: CREATED_AT, direction: DESC }) {
    nodes {
      id
      name
      createdAt
    }
  }
}
```

The `events` field in this query is a connection of type `EventConnectionWithTotalCount`. Every connection has the fields `pageInfo`, `nodes` and `edges`, and some connections (including the event connection) also provide additional fields such as `totalCount`.

{% hint style="info" %}
The number of items that can be returned in a single query is limited. Clients must be prepared to receive fewer items than requested.
{% endhint %}

{% hint style="info" %}
Connections can expose additional data about the relationship between the objects on their edges.

For example, the edges of the `affectedPeople` connection on the `IncidentAlert` type have a `distanceFromIncident` field that contains the distance in meters between the person and the event at the time of the alert.

If you do not need this information, you can select the `nodes` field instead of `edges` to remove one level of nesting from the API response.
{% endhint %}

To support pagination, the query must be changed and some fields from the `PageInfo` object must be added to the selections:

```graphql
query {
  events(first: 3, orderBy: { field: CREATED_AT, direction: DESC }) {
    pageInfo {
      endCursor
      hasNextPage
    }
    totalCount
    nodes {
      id
      name
      createdAt
    }
  }
}
```

The new query result now contains a cursor that can be used to fetch the next page:

```json
{
  "data": {
    "events": {
      "pageInfo": {
        "endCursor": "ed3qIy2/n4k=",
        "hasNextPage": true
      },
      "totalCount": 160724,
      "nodes": [...]
    }
  }
}
```

If there are more items in the connection (i.e. `hasNextPage` is `true`), the next three events can be fetched by passing the cursor value in the `after` argument:

```graphql
query {
  events(first: 3, after: "ed3qIy2/n4k=", orderBy: { field: CREATED_AT, direction: DESC }) {
    pageInfo {
      endCursor
      hasNextPage
    }
    totalCount
    nodes {
      id
      name
      createdAt
    }
  }
}
```

This process is repeated until `hasNextPage` is `false` and the entire collection has been traversed.

{% hint style="warning" %}
Do not change filter predicates or sorting halfway through the pagination process, as this will lead to undefined results.
{% endhint %}

## Object identification

Some types in X-Assist's GraphQL schema implement the `Node` interface which allows API clients to retrieve one or more objects by ID. Refer to the [Global Object Identification](https://graphql.org/learn/global-object-identification/) chapter in the GraphQL documentation for further information.

All applications must treat object IDs as opaque strings and make no assumptions about their content or length.

The encoding scheme of these IDs may also change over time. These changes are backwards compatible (X-Assist will continue to accept IDs in previous formats), but applications that store references to X-Assist objects are encouraged to include the `id` field in each query and update the IDs in their own data stores when a change is detected.

## Optimistic Concurrency Control

Many mutations use optimistic locking to prevent lost updates when multiple users make changes to existing data concurrently. The input objects for these mutations have a mandatory `version` field.

When performing a mutation, pass the expected version of the object as input and add the `version` field to your selections to get the new version of the object after the update. As an example, the mutation to update a site looks as follows:

```graphql
mutation {
  updateSite(input: {
    id: "U2l0ZS0yMTcx",
    version: "1",
    attributes: { ... }
  }) {
    site {
      version
    }
    errors: {
      message
      path
    }
  }
}
```

The response contains the new version of the site:

```json
{
  "data": {
    "updateSite": {
      "site": {
        "version": "2"
      },
      "errors": []
    }
  }
}
```

If a version mismatch occurs, you will receive an error instead:

```json
{
  "data": {
    "updateSite": {
      "site": null,
      "errors": [
        {
          "message": "Attempted to update a stale object: Site.",
          "path": [
            "input",
            "version"
          ]
        }
      ]
    }
  }
}
```

API clients can handle such errors by re-fetching the object (see [Object identification](#object-identification)) to get the current version, and then retrying the mutation.

{% hint style="warning" %}
Do not make assumptions about the content and the behavior of the version field.

In particular, do not assume that the version of new objects is equal to 1 or that updates only increment the version by one. Be aware that background processes or other events can also change the version of an object without any requests being sent to X-Assist from an API client.

For maximum robustness, treat the version as an opaque string.
{% endhint %}

## Data consistency

The X-Assist API provides different data consistency guarantees for different kinds of operations.

#### Mutations

GraphQL mutation payloads provide strong consistency. When you perform a mutation, all fields in the response are guaranteed to return the most recent data and reflect your changes.

However, this does not apply to changes caused indirectly by a mutation. For instance, when you register a new trip in X-Assist, the trip itself will be visible immediately, but the person's itinerary stops (expected locations) will only be updated after a delay because they are re-computed by an asynchronous background job.

#### Subscriptions

GraphQL subscriptions offer the same guarantees as mutations.

#### Queries

Unlike mutations, regular GraphQL queries only offer eventual consistency. This means that changes may not immediately visible to all clients, and that a query can return stale data.

## Rate limiting

Regular users and OAuth applications can make up to 1000 requests per hour, with a maximum number of 25 + 1 burst requests in rapid succession. This limit can be raised once your solution/integration has been validated by Crisis24.

The HTTP response headers will show your current rate limit status:

```http
HTTP/2 200
Date: Tue, 20 Apr 2021 10:56:29 GMT
X-RateLimit-Limit: 26
X-RateLimit-Remaining: 18
X-RateLimit-Reset: 50.5414
```

We loosely follow the [IETF draft](https://tools.ietf.org/id/draft-polli-ratelimit-headers-00.html) for rate limiting headers, but we do not use fixed-length time windows.

When a request is blocked due to rate limiting, X-Assist will respond with HTTP status 429 ("Too Many Requests"):

```http
HTTP/2 429
Date: Tue, 20 Apr 2021 11:10:12 GMT
X-RateLimit-Limit: 26
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 183.986299
Retry-After: 3.986299

You are rate limited, try again later.
```

| Response Header       | Description                                                                                                                                                                                                                                                                                                                                                           |
| --------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| X-RateLimit-Limit     | The maximum number of available burst requests when no previous requests have been made, or the previous requests occurred long enough ago.                                                                                                                                                                                                                           |
| X-RateLimit-Remaining | The number of requests that can be made immediately (i.e. the remaining burst request count).                                                                                                                                                                                                                                                                         |
| X-RateLimit-Reset     | The time in seconds until the full number of burst requests will be available again.                                                                                                                                                                                                                                                                                  |
| Retry-After           | <p>Only present when the request was blocked to due rate limiting.<br><br>For allowed requests that can be retried later (see note below), this header will contain the duration to wait in seconds before the request will be allowed.</p><p>When the request is forbidden due to complexity constraints, this header will have no value (i.e. an empty string).</p> |

{% hint style="warning" %}
Not all requests are equal. For example, a request with a very complex GraphQL operation may consume multiple "logical requests" at once.

It is therefore possible to create requests that will never succeed because they would consume more logical requests than the maximum number of available burst requests.
{% endhint %}


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.x-assist.crisis24.com/interfaces/api-access.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
