Building a Customer Card API
This page is intended for a technical audience that will be implementing a customer card API. Check out the customer cards page for an overview.
The high level architecture is:
Example cards
To get you started quickly, we've created a few example customer cards that you can configure and see how they look in your application. All example cards are available in our open source repository: team-plain/example-customer-cards
In the creating a customer card config section you can use these details to create customer card configs:
- URL: https://example-customer-cards.plain.com/
- Card key:
subscription-status
: a simple example where you see what plan a customer is oncustomer-details
: an example card with customer attributeslast-order
: an example card with the details of the most recent orderlatest-invoice
: a recent past due invoicesentry
: most recent Sentry errors with links to Sentry and logsusage
: an example card indicating the customer's usage in the current billing period
For more detail component documentation take a look at our Plain UI Components.
Protocol overview
Customer cards are not proactively loaded, all of it is just-in-time and pulled when required. This means that if your APIs are slow your users of the Support App will see a loading spinner while cards load.
- When a user in Plain opens up a customer's page the cards are loaded.
- Plain's backend figures out which cards can be returned from the cache and which cards need to be loaded. On first load of the customer this would be all cards.
- Then it figures out how many requests it needs to make (see request deduplication for details).
- Your APIs are then called with the customer's details, so you can look up the customer's data in your systems (see request section for details).
- Your APIs then return customer cards that consist of Plain UI components (see response section for details).
- The cards are cached based on either an explicit TTL value in the response or the default TTL in the card config.
- Card are shown to the user in Plain.
- Users can optionally reload the card at any time in which case only that one card will be requested from your API.
A few limits to be aware of:
- Your API must respond within 4 seconds, or it will time out. Please keep performance in mind when building your API.
- You can have a maximum of 25 customer cards configured.
- Card keys must be unique within a workspace. A key can only contain alphanumeric, hyphen, and underscore
characters
(regex:
[a-zA-Z0-9_-]+
).
The protocol allows for a lot of flexibility when deciding how many APIs you want to build and maintain.
You can decide to have only one API that aggregates data from your other systems. We'd recommend that the API only loads the absolutely necessary data needed to return a card to keep performance quick. That said, there's no harm in returning extra cards if it keeps your implementation simple or you've already loaded the data into memory.
Or you can decide to have many APIs for example each microservice serving its own set of cards. This allows each system to share with Plain what it knows about a customer and what context to display.
The choice is yours!
Creating a Customer Card config
To create a Customer Card head to Settings -> Customer Card and enter the following details:
- Title: this will be displayed as the title of the card so even if the card fails to load users know which card is errored.
- Key: the link between this config and your API. A key can only contain alphanumeric, hyphen, and underscore
characters (regex:
[a-zA-Z0-9_-]+
) - Default time to live (seconds): by default how long Plain should cache customer cards. Minimum is 15 seconds, maximum is 1 year in seconds (31536000 seconds).
- URL: the URL of your API endpoint that will be built to return customer cards. Must start with
https://
. - Headers (optional): the headers Plain should pass along when making the request. While this is optional it is highly recommend to add authorization headers or other tokens that authenticate the request as your API will be returning customer data.
Request
Plain will make the following request to your backend:
- Timeout: the request must complete within 4 seconds, or it will be terminated. See retry strategy for details on how timed out requests are retried.
- Method:
POST
- URL: the URL you configured on Customer Card settings page.
- Headers:
- All the headers you provided on Customer Card settings page. This should typically include authentication headers.
Content-Type
:application/json
Accept
:application/json
Plain-Workspace-Id
: the ID of the workspace the customer is in. This is useful for logging or request routing.User-Agent
:Plain/1.0 (plain.com; help@plain.com)
- Body:
cardKeys
: an array of card keys being requestedcustomer
: an object with the customer's core detailsemail
: the email of the customerexternalId
(optional): if the customer has anexternalId
then it will be sent, otherwise it isnull
.
Example request body:
{
"cardKeys": [
"plan-details",
"subscription-status"
],
"customer": {
"email": "alex@grocery.co",
"externalId": "your_user_id_795BFCD5-130F-4E72-BD46-14F717BE0830"
}
}
Request deduplication
If you configure multiple customer cards that have the same API details then Plain will batch them and make only one request.
The request deduplication logic for customer card configs is:
- The following config properties are ignored: Title, Card key, Default TTL
- API URL: leading and trailing whitespaces are trimmed, the rest is treated case sensitively and as is
- For example these URLs would be treated as different:
https://api.example.com/cards
https://api.example.com/cards/
https://api.example.com/Cards
- For example these URLs would be treated as different:
- API Headers: order of headers in config does not matter
- Name: leading and trailing whitespaces are trimmed and treated case insensitively
- For example these header names be treated the same:
Authorization
AUTHORIZATION
authorization
- For example these header names be treated the same:
- Value: no processing done, treated as is (be careful with any extra whitespace characters)
- For example these header values would be treated as different:
Bearer my-token
bearer my-token
bearer my-token
- For example these header values would be treated as different:
- Name: leading and trailing whitespaces are trimmed and treated case insensitively
Response
For each key requested a corresponding card MUST be returned in the response, otherwise an integration error will be returned for that card.
Any extra cards in the response will be ignored.
Your API must respond with a 200 status code or the response body won't be process and will be treated as an error.
The response body must be a JSON object with:
cards
: an array of cards. EverycardKey
requested should have a correspondingkey
returned. Any extra returned cards will be ignored.key
: the requested keytimeToLiveSeconds
(optional, nullable): can either be omitted ornull
. If provided it will override the default time to live value. This allows you to control caching on a case-by-case basis.components
(nullable):null
to indicate that the card has no data or an array of Plain UI Components.
Example response body for a card cached for 1 hour:
{
"cards": [
{
"key": "plan-details",
"timeToLiveSeconds": 86400,
"components": [
{
"componentRow": {
"rowMainContent": [
{
"componentText": {
"text": "Plan",
"textColor": "MUTED",
"textSize": "M"
}
}
],
"rowAsideContent": [
{
"componentBadge": {
"badgeLabel": "Starter",
"badgeColor": "YELLOW"
}
}
]
}
}
]
}
]
}
Example response body for a card that has no data and should not be displayed and TTL omitted:
{
"cards": [
{
"key": "plan-details",
"components": null
}
]
}
Retry strategy
Errors are classified into two categories:
- Retriable errors: these are transient issues where retrying once is appropriate
- Integration errors: these are typically programming or configuration errors. These errors won't be retried and cached for 5 minutes.
Retriable errors
The following errors are retried once after a 1-second delay:
- HTTP 5xx response status code
- HTTP 429 Too Many Requests response status code
- The request times out after 4 seconds.
- Plain fails to make the request for some reason
Retriable errors are not cached, therefore if the cards are requested again via the Support App they will be re-requested.
Integration errors
The following errors are not retried:
- All HTTP 4xx response status codes except for HTTP 429 Too Many Requests response status code.
- A card key is missing in the response, for example if
subscription-details
is requested but thecards
array in the response doesn't have an element withsubscription-details
key. - The response body does not match the expected schema documented in response.
Integration errors are cached for 5 minutes and usually indicate a programming or configuration error. Users have the ability to manually refresh a card in which case the card will be requested again.
If you have any problems, please get in touch with us by email on help@plain.com, and we will be happy to help.