Skip to main content

Sync Customers Programmatically

When you embed Prismatic into your app, you need to authenticate your customer users as users of specific customers in Prismatic. You can establish these customer records in one of two ways:

  1. Create them programmatically through the API. When authenticating embedded customer users, ensure that your customer record's externalId matches your embedded JWT's customer claim.
  2. Specify both externalId and name as embedded JWT claims. With this approach, customer records will be automatically created if they don't already exist.

This guide focuses on option 1, demonstrating how to programmatically create and manage customer records rather than relying on automatic creation from your embedded application. You'll use External IDs, which provide a way to link your external customer identifiers with Prismatic customer records.

Setting up

This tutorial uses Python, though you can adapt the same ideas to any language that has a GraphQL client library. Full code shown in this tutorial can be found on GitHub.

To start, add gql to your dependencies for your Python project:

pip install gql

Next, import the necessary libraries and create a GraphQL client so you can run queries against Prismatic's API. Define the transport layer for getting to the API (HTTP), which will include an authorization header to authenticate your client against Prismatic:

Initialize GraphQL client with authentication
import json
import os
import sys
from gql import gql, Client
from gql.transport.requests import RequestsHTTPTransport

token = os.environ['PRISMATIC_API_KEY']
api_endpoint = "https://app.prismatic.io/api/"

transport = RequestsHTTPTransport(
url=api_endpoint,
headers={'Authorization': f'Bearer {token}'}
)
client = Client(transport=transport)

This code assumes that an environment variable, PRISMATIC_API_KEY, has been set (it's best practice not to hard-code API keys). To get an API key into your environment variables, you can use the me:token prism subcommand:

export PRISMATIC_API_KEY=$(prism me:token)

Now that you have a working client, you can write some GraphQL queries and mutations to manage customers within Prismatic:

GraphQL queries and mutations

To sync customers, you'll use the customers query and createCustomer and deleteCustomer mutations.

GraphQL Queries and Mutations

In GraphQL lingo, a query is similar to a SELECT statement in SQL. You query for a particular set of information in a read-only way.

A mutation is similar to an INSERT, UPDATE, or DELETE statement in SQL. You mutate data by creating new records, or updating or deleting existing records.

List all customers

First, write a function that gets a list of all your customers, including their name, description, Prismatic id, and externalId:

Query all customers
def getCustomers():
query = gql("""
query {
customers {
nodes {
id
name
description
externalId
}
}
}
""")
result = client.execute(query)
return result['customers']['nodes']

This function runs a customers query against Prismatic's API.

The query returns an object with a customers key, which has (in GraphQL lingo) has a series of nodes (customers). The function returns result['customers']['nodes'] so that just a list of customer objects are returned.

Invoke this function and use json.dumps() for readability:

print(json.dumps(getCustomers()))
[
{
"id": "Q3VzdG9tZXI6MThjZTBjM2EtYmQ5NS00MWJiLWIyMjUtN2MwYjVjMDg3YmE4",
"name": "Mars Missions",
"description": "Mars Missions Corp",
"externalId": "abc-123"
},
{
"id": "Q3VzdG9tZXI6MDllZDQyNTctMTNkMS00YTY4LWFiNTktY2Y5NzNmZGUyOTQy",
"name": "Eastern Space Flight",
"description": "Eastern Space Flight - Houston, TX",
"externalId": "xyz-456"
}
]

You can use the output of this function to figure out which customers have not been synced into Prismatic, or to sync Prismatic customers back to an external system.

GraphQL pagination

Before moving on, let's address pagination. By default, the Prismatic API returns the first 100 results for a query. So, the customers query only returns 100 customers, even if you have more than 100 in the system.

If you want to download all customers, you'll need to update your getCustomers() function a bit. Update your query so that it requests pagination information (pageInfo). Request whether or not there are additional pages to fetch (hasNextPage), and a unique ID indicating where to start the next page (endCursor). Feed that endCursor back in to your query as a parameter after, so the GraphQL API knows where it last left off:

Query all customers with pagination
def getCustomers():
query = gql("""
query ($startCursor: String){
customers(after: $startCursor) {
nodes {
id
name
description
externalId
}
pageInfo {
hasNextPage
endCursor
}
}
}
""")

cursor = ""
hasNextPage = True
customers = [] # Used to accumulate customer objects

while hasNextPage:
result = client.execute(query, variable_values={"startCursor": cursor})
customers += result['customers']['nodes']
hasNextPage = result['customers']['pageInfo']['hasNextPage']
cursor = result['customers']['pageInfo']['endCursor']

return customers

Fetch a specific customer by external ID

Now, you can look up a specific customer by their externalId. The customers query allows you to filter customers by externalId. The function passes in the externalId as a variable to the customers query using an object, params, that contains the GraphQL variables to use. The function also asserts that exactly one customer object is returned from the API:

Fetch a customer by external ID
def getCustomerByExternalId(externalId):
query = gql("""
query ($externalId: String!) {
customers (externalId: $externalId) {
nodes {
id
name
description
externalId
}
}
}
""")
params = {"externalId": externalId}
response = client.execute(query, variable_values=params)
assert len(response["customers"]["nodes"]) == 1, f"No customer with external ID '{externalId}' exists."
return response["customers"]["nodes"][0]

If you invoke this function now, you'll get a single customer based on the externalId that you provide:

print(json.dumps(getCustomerByExternalId("abc-123")))
{
"id": "Q3VzdG9tZXI6MThjZTBjM2EtYmQ5NS00MWJiLWIyMjUtN2MwYjVjMDg3YmE4",
"name": "Mars Missions",
"description": "Mars Missions Corp",
"externalId": "abc-123"
}

Create a new customer

Next, write a function that creates a new customer given the customer's name, description, and externalId. To do that, use the createCustomer GraphQL mutation. Similar to the getCustomerByExternalId function above, supply some input variables to your mutation with a params object:

Create a new customer
def createCustomer(name, description="", externalId=""):
mutation = gql("""
mutation($name: String!, $description: String, $externalId: String) {
createCustomer(
input: { name: $name, description: $description, externalId: $externalId }
) {
customer {
id
name
description
externalId
}
errors {
messages
}
}
}
""")

params = {
"name": name,
"description": description,
"externalId": externalId
}

result = client.execute(mutation, variable_values=params)

if result["createCustomer"]["errors"]:
raise Exception(result["createCustomer"]["errors"])
else:
return result["createCustomer"]["customer"]

The createCustomer mutation will return errors if the name or externalId you specified is already in the Prismatic system. If the mutation does not throw an error, it will return a customer object containing the new customer's name, description, externalId and generated Prismatic id. Let's try it out:

print(
json.dumps(
createCustomer(
name="Rockets Rockets Rockets",
description="Rockets^3",
externalId="456-xyz"
)))
{
"id": "Q3VzdG9tZXI6NGQ3ZDc3ZTktOTllNy00NmJiLWFlNDktMTg1N2JlNWNiYjUz",
"name": "Rockets Rockets Rockets",
"description": "Rockets^3",
"externalId": "456-xyz"
}

Delete a customer by external ID

For the last customer-related function, delete a customer given their external ID. To do that, use the deleteCustomer mutation. First, the function looks up the customer to delete using the getCustomerByExternalId function written previously. Then, the function feeds the Prismatic ID into the deleteCustomer mutation:

Delete a customer by external ID
def deleteCustomer(externalId):
customer = getCustomerByExternalId(externalId)
mutation = gql("""
mutation ($id: ID!) {
deleteCustomer(
input: {
id: $id
}
) {
customer {
id
name
description
externalId
}
errors {
messages
}
}
}
""")

params = {"id": customer["id"]}

result = client.execute(mutation, variable_values=params)

if result["deleteCustomer"]["errors"]:
raise Exception(result["deleteCustomer"]["errors"])
else:
return result["deleteCustomer"]["customer"]

If you supply an external ID that is not attached to a customer, the getCustomerByExternalId() function will throw an error indicating that.