Skip to main content

Using Prismatic's API

As you incorporate Prismatic into your development ecosystem, it's handy to be able to wrap Prismatic's GraphQL-based API so you can programmatically manage users, customers, instances, monitors, alerting and more. On this page you will find examples of how to wrap common Prismatic GraphQL queries and and mutations.

This page doesn't cover every query or mutation that Prismatic supports, but should give you a good idea of how to do standard CRUD (create, read, update, delete) operations using the Prismatic API.

Before you begin, please read through our Intro to Prismatic's API article and generate an API token for yourself, so you can make API calls.

Managing Customers Through the API#

Let's start by managing customers programmatically. If you'd like to see examples of these queries and mutations in code, check out this python script that wraps many of the customer-related queries and mutations.

Querying Customers Through the API#

If you know the ID of the customer you would like to query, use the customer query. The myCustomerId (you can call it anything) query variable that you pass into the query is substituted in for customer(id), and a customer with the given ID is returned:

Fetch a Specific Customer
query ($myCustomerId: ID!) {  customer(id: $myCustomerId) {    id    name    externalId  }}
Query Variables
{  "myCustomerId": "Q3VzdG9tZXI6ZDM0NTZjZWItNTNlOS00YmI5LWFhODItN2QyZDQ3YjZkMTA4"}
Try It Out ❯
Use Query Variables

While you can do some string concatenation or hard-code customer IDs, instance variable values, etc., to create a query, we recommend that you instead leverage GraphQL query variables. The examples below use query variables.

If you would like information about several customers, use the customers query instead:

Fetch All Customers
query exampleGetCustomers {  customers(isSystem: false) {    nodes {      id      name      externalId    }  }}
Try It Out ❯

The isSystem: false property above removes system-generated customers that are used when testing integrations in the integration designer.

Adding a New Customer Through the API#

To add a new customer to your account, use the createCustomer mutation. (It's called a mutation and not a query because you are mutating data, not just accessing it). We'll make heave use of query variables for this mutation:

Create a New Customer
mutation ($customerName: String!, $customerDescription: String, $customerExternalId: String) {  createCustomer(    input: {name: $customerName, description: $customerDescription, externalId: $customerExternalId}  ) {    customer {      id    }    errors {      field      messages    }  }}
Query Variables
{  "customerName": "FTL Rockets",  "customerDescription": "Faster-than-light Rocket Company, LLC",  "customerExternalId": "abc-123"}
Try It Out ❯

In the createCustomer example above, we capture the id of the customer that is created, as well as errors (if any) that are thrown. The API might throw an error if a customer with the same name or external ID already exists.

Updating a Customer Through the API#

You can update a customer using the updateCustomer mutation. You must know the ID of the customer you want to update. In this example, the ID of the customer and the new information that we want to change are passed in as query variables:

Update a Customer
mutation ($customerId: ID!, $newDescription: String) {  updateCustomer(input: {id: $customerId, description: $newDescription}) {    errors {      field      messages    }  }}
Query Variables
{  "customerId": "Q3VzdG9tZXI6ZDM0NTZjZWItNTNlOS00YmI5LWFhODItN2QyZDQ3YjZkMTA4",  "newDescription": "My Updated Description"}
Try It Out ❯

Deleting a Customer Through the API#

The deleteCustomer mutation takes a customer ID, and returns the customer (if it has been deleted), or an error if the customer can't be found.

Delete a Customer
mutation ($customerId: ID!) {  deleteCustomer(input: {id: $customerId}) {    customer {      name    }    errors {      field      messages    }  }}
Query Variables
{  "customerId": "Q3VzdG9tZXI6ZDM0NTZjZWItNTNlOS00YmI5LWFhODItN2QyZDQ3YjZkMTA4"}
Try It Out ❯

Managing Users Through the API#

There are two types of users: your customer's users and your organization's team members. If you're using the embedded marketplace and signed JWTs to seamlessly authenticate customer users, you probably won't need to manage your customer users programmatically. It's more likely that you'll manage organization users, but we'll look at how to manage both.

Creating a User Through the API#

In order to create a new user, you will need to know the user's email address, and role. You can optionally include the user's name and phone number.

First, look up the roles you can assign a user using either the customerRoles or organizationRoles query:

Query for Organization Roles
query {  organizationRoles {    id    name    permissions {      nodes {        name        description      }    }  }}
Try It Out ❯

This will give you a series of roles along with their IDs and the permissions that are associated with them. Find an appropriate role for the user you want to add, and note its ID. Next, run the either the createCustomerUser or createOrganizationUser mutation to create the user:

Create an Organization Team Member
mutation ($email: String!, $name: String, $externalId: String, $role: ID!) {  createOrganizationUser(    input: {email: $email, name: $name, externalId: $externalId, role: $role}  ) {    user {      id      name      email      externalId    }    errors {      field      messages    }  }}
Query Variables
{  "email": "lisa.nguyen@smith-rockets.co",  "name": "Lisa Nguyen",  "externalId": "ABCCBC0B-98D0-453B-A795-0B430EFBF020",  "role": "Um9sZTo0NzdmNGRlYi03NzRlLTQ0M2UtOWY0MS01OGRmOWNhZjllNmM="}
Try It Out ❯

Users who are created in this way are immediately sent an email asking them to set a password for Prismatic.

Listing Users by Customer Through the API#

One big advantage of GraphQL is that you can query resources for related data. Once again we'll use the customers query to list each customer's ID and name, but in addition we'll list the users attached to the customer. Specifically, we'll list each customer user's ID, email address, name, and external ID:

List All Customer Users by Customer
query {  customers(isSystem: false) {    nodes {      id      name      users {        nodes {          id          email          name          externalId        }      }    }  }}
Try It Out ❯

Updating and Deleting Users Through the API#

You can accomplish the other CRUD operations (update and delete) using the updateUser and deleteUser mutations, similar to how you update or delete customers

Deploying New Instances Through the API#

Find Integrations Through the API#

Before deploying an instance of an integration, you'll need to know the ID of the version of the integration you want to deploy. You can use the integrations query to list integrations. You'll want to deploy a specific version of the integration, so including the allVersions: true and versionIsAvailable: true parameters will show you all available published versions of your integration (not just your canonical integration ID).

This query also uses sortBy, so results are sorted by version, and the latest version is returned first.

Find all available versions of the Acme Inventory integration
query ($integrationName: String!) {  integrations(    name: $integrationName    allVersions: true    versionIsAvailable: true    sortBy: {direction: DESC, field: VERSION_NUMBER}  ) {    nodes {      id      name      versionNumber    }  }}
Query Variables
{  "integrationName": "Acme Inventory"}
Try It Out ❯

Creating a New Instance Through the API#

The createInstance mutation requires four pieces of information:

  • The ID of a customer this instance is for. This can be found through the customers query.
  • The ID of the version of the integration you want to deploy. This can be found through above query.
  • A name for your instance.

Once you've collected that information, you can create your instance:

Create a New Instance of an Integration
mutation ($customerId: ID!, $integrationId: ID!, $name: String!) {  createInstance(    input: {customer: $customerId, integration: $integrationId, name: $name}  ) {    instance {      id      name      flowConfigs {        nodes {          flow {            name          }          webhookUrl        }      }    }    errors {      field      messages    }  }}
Query Variables
{  "customerId": "Q3VzdG9tZXI6OThiMjU3MDUtZmMzNC00NWYwLTk0ZDItODA0ZjFkYzEyYTZk",  "integrationId": "SW50ZWdyYXRpb246ZjY1Y2I5YTktMmZiZC00ZGE0LWIwYzktMjQ4Njc0YTY2NGMz",  "name": "Acme Inventory"}
Try It Out ❯

This query returns some additional data about the instance, including a list of flows and their respective webhook URLs for invoking the flows.

Config variables can be set during the createInstance mutation. They can also be set with an updateInstance or updateInstanceConfigVariables mutation, which we'll address below. If you'd like to set the config variables as part of the createInstance mutation, follow the same pattern that we show in the updateInstance mutation below.

Updating Instance Config Variables Through the API#

If you're programmatically updating an instance, you're likely updating the instance's config variable values. There are two mutations that you can use to update instance config variables:

  • updateInstance lets you update multiple properties of an instance (like name, description, config variables, etc.). Note, though, that this mutation sets values for all config variables whether or not you specify a value for them. If you omit a config variable from the mutation, its value will be reset to its default value (or null if no default is configured). Use this mutation with caution.

    Setting config variables with updateInstance

    When you programmatically set config variables using the updateInstance mutation, all config variables must be given a value. If you omit a config variable, that config variable will be set to its default value (even if it was previously set).

    If you'd like to set just a subset of config variables while leaving the others alone, use the updateInstanceConfigVariables mutation, described below.

  • updateInstanceConfigVariables lets you safely update specific instance config variables, while leaving the other config variables untouched. Use this mutation if you would like to modify one (or a few) values.

Updating Config Variables with updateInstance#

Before the updateInstance mutation, let's query the instance for its current config variables.

Each config variable is tied to a requiredConfigVariable which has a key (like Acme Inventory Endpoint) and a defaultValue. Non-connection config variables also have a value property, which is the customer-specific value you'd like to set for that config variable (like https://app.acme.com/api).

If your config variable is a connection, which usually represents a username, password, API key, OAuth 2.0 connection, the shape is a bit different. It'll be comprised of a set of inputs that have a name, type, and value.

Get an Instance's Required Config Variables
query ($instanceId: ID!) {  instance(id: $instanceId) {    id    name    configVariables {      nodes {        requiredConfigVariable {          key          collectionType          dataType          defaultValue        }        value        inputs {          nodes {            name            type            value          }        }      }    }  }}
Query Variables
{  "instanceId": "SW5zdGFuY2U6MWY0NmRmM2MtM2FjMi00ODMwLWExODEtZjNhN2FmN2RkNTRi"}
Try It Out ❯

If you run the query above, you'll get back something similar to the JSON below. Note that the different config variables each returned something slightly different, depending on the type of config variable they represent:

  • Acme Inventory Endpoint is a plain string and returned a value of "" (empty string).
  • Sync Inventory Item Metadata? is a boolean toggle and returned a value of "false".
  • Inventory Field Mapping is a key-value list, and returned a value of "[]" (empty array).
  • Acme Inventory User/Pass is a connection with two inputs - username and password which have blank values of their own.
Get an Instance's Required Config Variables - Response
{  "data": {    "instance": {      "id": "SW5zdGFuY2U6ODgyOTQwM2MtOGZkYS00ZGMwLTg3OGYtYWI5MWNhMmVmMTVj",      "name": "Acme Inventory",      "configVariables": {        "nodes": [          {            "requiredConfigVariable": {              "key": "Inventory Field Mapping",              "collectionType": "KEYVALUELIST",              "dataType": "STRING",              "defaultValue": "[]"            },            "value": "[]",            "inputs": {              "nodes": []            }          },          {            "requiredConfigVariable": {              "key": "Sync Inventory Item Metadata?",              "collectionType": null,              "dataType": "BOOLEAN",              "defaultValue": "false"            },            "value": "false",            "inputs": {              "nodes": []            }          },          {            "requiredConfigVariable": {              "key": "Acme Inventory User/Pass",              "collectionType": null,              "dataType": "CONNECTION",              "defaultValue": null            },            "value": null,            "inputs": {              "nodes": [                {                  "name": "password",                  "type": "VALUE",                  "value": ""                },                {                  "name": "username",                  "type": "VALUE",                  "value": ""                }              ]            }          },          {            "requiredConfigVariable": {              "key": "Acme Inventory Endpoint",              "collectionType": null,              "dataType": "STRING",              "defaultValue": ""            },            "value": "",            "inputs": {              "nodes": []            }          }        ]      }    }  }}

Once we know the shape of the config variables (which ones are strings, which ones are key-value lists, and which ones are connections, etc), we can send back a config variable object that matches that shape. We use values to set connection config variables, and for connections and key-value lists we serialize the name-value or key-value pairs as JSON strings:

Update an Instance With New Configuration Values with updateInstance
mutation ($instanceId: ID!, $configVariables: [InputInstanceConfigVariable]) {  updateInstance(input: {id: $instanceId, configVariables: $configVariables}) {    instance {      id      name      configVariables {        nodes {          requiredConfigVariable {            key          }          value          inputs {            nodes {              name              value            }          }        }      }    }    errors {      field      messages    }  }}
Query Variables
{  "instanceId": "SW5zdGFuY2U6MWY0NmRmM2MtM2FjMi00ODMwLWExODEtZjNhN2FmN2RkNTRi",  "configVariables": [    {      "key": "Acme Inventory User/Pass",      "values": "[{\"name\":\"username\",\"type\":\"value\",\"value\":\"my.username\"},{\"name\":\"password\",\"type\":\"value\",\"value\":\"Pa$$W0Rd\"}]"    },    {      "key": "Acme Inventory Endpoint",      "value": "https://app.acme.com/api"    },    {      "key": "Sync Inventory Item Metadata?",      "value": "true"    },    {      "key": "Inventory Field Mapping",      "value": "[{\"key\":\"qty\",\"value\":\"Quantity\"},{\"key\":\"usd\",\"value\":\"Price\"}]"    }  ]}
Try It Out ❯

Updating Config Variables with updateInstanceConfigVariables#

This mutation lets you set values for a subset of config variables, while keeping the others untouched. In this example, we set a string value for a config variable named My String, a list of values for a list config variable named My List, and a couple of values for a connection config variable named Amazon S3 Connection which has a couple of connection inputs:

Update an Instance With New Configuration Values with updateInstanceConfigVariables
mutation ($instanceId: ID!, $configVariables: [InputInstanceConfigVariable]) {  updateInstanceConfigVariables(    input: {id: $instanceId, configVariables: $configVariables}  ) {    instance {      id    }    errors {      field      messages    }  }}
Query Variables
{  "instanceId": "SW5zdGFuY2U6MWY0NmRmM2MtM2FjMi00ODMwLWExODEtZjNhN2FmN2RkNTRi",  "configVariables": [    {      "key": "My String",      "value": "My Value"    },    {      "key": "My List",      "value": "[\"Value 1\",\"Value 2\"]"    },    {      "key": "Amazon S3 Connection",      "values": "[{\"name\":\"accessKeyId\",\"type\":\"value\",\"value\":\"MyAccessKey\"},{\"name\":\"secretAccessKey\",\"type\":\"value\",\"value\":\"MySecretAccessKey\"}]"    }  ]}
Try It Out ❯

Deploying an Instance Through the API#

An instance needs to be deployed in order for your new configuration to take effect. To deploy an instance, use the deployInstance mutation:

Deploy an Instance
mutation ($instanceId: ID!) {  deployInstance(input: {id: $instanceId}) {    instance {      name      enabled      deployedVersion      lastDeployedAt    }    errors {      field      messages    }  }}
Query Variables
{  "instanceId": "SW5zdGFuY2U6ODgyOTQwM2MtOGZkYS00ZGMwLTg3OGYtYWI5MWNhMmVmMTVj"}
Try It Out ❯

Fetching Step Results Through the API#

When you invoke an integration asynchronously, an executionId is returned:

curl https://hooks.prismatic.io/trigger/EXAMPLE== \    --data '{}'  \    --header "Content-Type: application/json"
{"executionId":"SW5zdGFuY2VFeGVjdXRpb25SZXN1bHQ6OTdiNWQxYmEtZGUyZi00ZDY4LWIyMTgtMDFlZGMwMTQxNTM5"}

You can use the executionResult along with that executionId to fetch the results of that execution.

Fetch Execution Results
query ($executionId: ID!) {  executionResult(id: $executionId) {    startedAt    endedAt    error    stepResults (stepName: "codeBlock") {      nodes {        stepName        resultsUrl      }    }  }}
Query Variables
{  "executionId": "SW5zdGFuY2VFeGVjdXRpb25SZXN1bHQ6OTdiNWQxYmEtZGUyZi00ZDY4LWIyMTgtMDFlZGMwMTQxNTM5"}
Try It Out ❯

If the execution is still running, the endedAt property will be null. You can query for stepResults, and specifically ask for the resultsUrl of a specific step. Step names are "camelCased" in the stepResults filter (so, My Code Block -> myCodeBlock). Step results are written out to Amazon S3, and the resultsUrl returns a short-lived presigned URL where you can fetch the step's results.

Unpacking Step Results

A step can return anything. They often return javascript objects or JSON, but some steps return binary files like PDFs or MP3s or a JavaScript Buffer. To account for the variety of things that a step can return, Prismatic uses msgpack to package up step results. The step result file you download from S3 will appear encoded, but after "unpacking" the contents, it'll look like normal JSON (or the type of data your step yielded).

To "unpack" step results, use msgpack.decode() in NodeJS (or a similar function in a language of your choosing - msgpack supports many languages).

Pagination with Prismatic's API#

By default, most queries return a maximum of 100 results. If you have more than 100 records of a particular type (say, more than 100 instances), you can pull down 100, process those results, and then ask for the next 100 results, etc., until you process all records.

To request page info, add pageInfo alongside the nodes of any query. pageInfo has several properties - the two you'll likely be interested in are hasNextPage, which tells you if more results are available, and endCursor, which is an indicator of where your query "left off", so your next query can pick up from there.

Fetch the first page of 100 instances
query getPageOfInstances($cursor: String) {  instances(after: $cursor) {    nodes {      id      name    }    pageInfo {      hasNextPage      endCursor    }  }}
Query Variables
{  "cursor": ""}
Try It Out ❯

After fetching your results, you can check if pageInfo.hasNextPage is true. If it is, grab the pageInfo.endCursor (which might read something like YXJyYXljb25uZWN0aW9uOjEwMA==) and run the same query, changing your query variables to read

{ "cursor": "YXJyYXljb25uZWN0aW9uOjEwMA==" }

That cursor query variable will be used by the query above as the after property. That will cause the API to fetch up to 100 more results, but instead of starting at the beginning the API will start where your previous query left off.

Other CRUD Operations Through the API#

This article covers some common GraphQL queries and mutations that you might encounter, but there are dozens of other queries and mutations that are not covered here. For information on other queries and mutations that are available, see the sidebar to the left. Each query and mutation has its own documentation page with details on what inputs they expect, and what objects are returned.