GraphQL API

Overview

Flat's API lets you read and write your organization's data programmatically. You can use it to import data into Flat, generate custom reports, build automations driven by events in other apps, and more.

The API is based on GraphQL. Unlike a REST API, a GraphQL API has just a single endpoint for all requests, both reads and writes, and it gives you full control over the shape of the data you get in response. If you're new to GraphQL, we recommend checking out the official GraphQL documentation to learn the basics.

The API is currently in beta and undergoing rapid development. We don't expect there to be breaking changes, but if there are, we'll keep you in the loop.

Endpoint and schema

Flat's GraphQL API endpoint is:

https://api.flat.app/v1/beta/graphql

The endpoint supports GraphQL introspection, so the easiest way to explore the API schema and take it for a spin is via a GraphQL client.

Alternatively, you can view the schema here in SDL format.

If you don't already have a preferred GraphQL client, we recommend checking out Insomnia, Postman, or GraphiQL.

Authentication and authorization

You authenticate to the API using a bearer token. Just include the HTTP Authorization header in all of your API requests:

Authorization: Bearer YOUR_API_TOKEN

Currently, the API supports personal access tokens. A personal access token is tied to a particular user. It has access to the same workspaces and topics as the user, and any mutations made using it are attributed to the user.

To request your personal access token, reach out to us through one of our support channels.

Operations

The API supports two kinds of operations: queries and mutations.

Queries

Queries fetch data from your organization and are read-only. The API supports:

  • Fetching individual objects by ID, e.g., getting a specific topic

  • Listing whole classes of objects, e.g., all accessible workspaces in your organization

  • Traversing the relationships between objects, e.g., getting a topic along with its labels, owner, collaborators, etc.

Mutations

Mutations modify data in your organization — for example, creating or updating a topic is a mutation. Every mutation returns a reference to the object that was created or updated. That way, you can retrieve information about the mutated object, such as getting a newly created object's system-generated ID.

Making your first request

Request

To make a request to the API, just issue a POST request with a JSON object as the body, structured like this:

{
  "query": "query { ... }",
  "variables": { "variable1": "value1", "variable2": "value2" }
}

The properties are:

  • query: required property that contains a GraphQL document describing one or more operations (queries or mutations)

  • variables: optional property that contains a mapping of variables and values to substitute into the GraphQL document

  • operationName: optional property with the name of the GraphQL operation to execute (required if query defines more than one operation)

Below is an example JSON body requesting a topic's description and title, using a variable for the topic ID.

{
  "query": "query($id: ID!) { topic(id: $id) { description title } }",
  "variables": { "id": "TOPIC_ID" }
}

Here's an example of a complete request that retrieves info about all accessible topics in your organization.

curl --request POST \
  --url https://api.flat.app/v1/beta/graphql \
  --header 'Content-Type: application/json' \
  --header "Authorization: Bearer YOUR_API_TOKEN" \
  --data '{"query":"query { topics { nodes { id description title owner { id displayName } } } }"}'

Response

In GraphQL, the response data is always structured to match the shape of your request.

In the example request above, the response would look like this:

{
  "data": {
    "topics": {
      "nodes": [
        {
          "id": "TOPIC_ID_1",
          "description": null,
          "title": "Title of the topic",
          "owner": null
        },
        {
          "id": "TOPIC_ID_2",
          "description": "Description of the topic",
          "title": "Title of the topic",
          "owner": {
            "id": "USER_ID_1",
            "displayName": "Foobar"
          }
        }
      ]
    }
  }
}

Working with the API

Pagination

For API fields that return a list of objects, the API will return up to 50 objects per request. To get more results, the API supports forward and backward cursor-based pagination following the Relay "Connections" spec. You can learn more about the reasoning behind this approach in the official GraphQL documentation.

Let's say you want to get all of the accessible topics in your organization. You could execute the query below to get the first page of 50 topics.

# Note: "first" and "after" arguments are optional in this example.
# They're included with their default values for clarity. 
query {
  topics(first: 50, after: null) {
    edges {
      cursor
      node {
        id
        title
      }
    }
    pageInfo {
      hasNextPage
      endCursor
    }
  }
}

To get the next page, take the value of pageInfo.endCursor in the response and pass it as the value of after in the next request. When pageInfo.hasNextPage is false in the response, you've retrieved all the results.

If you don't need any of the edge fields, you can simplify the query and jump directly to the nodes like this:

query {
  topics(first: 50, after: null) {
    nodes {
      id
      title
    }
    pageInfo {
      hasNextPage
      endCursor
    }
  }
}

Pagination direction

Lists of objects are returned in order of creation time. The example above would retrieve the oldest topics first. To get the newest objects first, you can paginate backwards instead of forwards using the corresponding arguments and fields:

Pagination directionArgumentsFields

Forwards

first, after

endCursor, hasNextPage

Backwards

last, before

startCursor, hasPreviousPage

Filtering

Most API fields that return a list of objects support filtering. Here's an example query to retrieve archived topics with the label "bug":

query {
  topics(filter: { labels: ["bug"], states: [archived] }) {
    nodes {
      id
      title
    }
  }
}

By default, the API only returns "active" entities, e.g., topics that have not been archived or trashed, workspaces that have not been archived, etc. However, most filters have a states field that lets you override the default and specify which state(s) to include.

Examples

Below are some example GraphQL documents for common use cases.

Keep in mind:

  • These are example GraphQL documents, not complete API requests. See Making your first request to form a complete API request from a GraphQL document.

  • In these examples, the values are hard-coded into the GraphQL document for illustration. However, you'll typically want to use variables to parameterize your queries and mutations for easy reuse.

To see all of the queries and mutations that the API supports, instrospect the API using a GraphQL client like Insomnia, Postman, or GraphiQL.

Queries

Fetching info about workspaces and their stages

query {
  workspaces {
    nodes {
      id
      displayName
      
      stages {
        nodes {
          id
          displayName
          position
        }
      }
    }
  }
}

Fetching info about users

query {
  users {
    nodes {
      id
      disabledAt
      displayName
      fullName
    }
  }
}

Fetching info about a single topic

query {
  topic(id: "TOPIC_ID") {
    id
    description
    dueDate
    position
    title
    
    collaborators {
      nodes {
        id
        displayName
      }
    }
    
    stage {
      id
      displayName
    }
    
    labels {
      nodes {
        color
        text
      }
    }
    
    owner {
      id
      displayName
    }
    
    workspace {
      id
      displayName
    }
  }
}

Mutations

Creating a topic

mutation {
  topicCreate(input: {
    position: 3.14159265
    stageId: "STAGE_ID"
    title: "Title"
  }) {
    topic {
      id
    }
  }
}

Updating a topic's basic details

mutation {
  topicUpdate(
    id: "TOPIC_ID"
    input: {
      description: "New description"
      dueDate: "2024-04-08"
      title: "New title"
    }
  ) {
    topic {
      id
    }
  }
}

Updating a topic's owner

mutation {
  topicUpdate(
    id: "TOPIC_ID"
    input: {
      ownerId: "USER_ID"
    }
  ) {
    topic {
      id
    }
  }
}

Updating a topic's collaborators

mutation {
  topicUpdateCollaborators(
    id: "TOPIC_ID"
    collaboratorIdsToAdd: ["USER_ID_1", "USER_ID_2"]
    collaboratorIdsToRemove: ["USER_ID_3"]
  ) {
    topic {
      id
    }
  }
}

Updating a topic's labels

mutation {
  topicUpdateLabels(
    id: "TOPIC_ID"
    labelsToAdd: [{ color: "RED", text: "bug" }]
    labelsToRemove: [{ color: "blue", text: "feedback" }]
  ) {
    topic {
      id
    }
  }
}

Support

If you have any questions or issues with the API, just reach out to us through one of our support channels.

Last updated