Creating a custom timeline entry
This recipe assumes you've already familiarized yourself with the Core API getting started, Authentication, and Error Handling pages.
Custom timeline entries are events specific to your business that represent important context when helping customers. For example:
- Subscription changes
- Account level changes such as email changes or password resets
- Custom contact forms for getting in touch, bug reports, feature requests, etc.
- Logging outages or errors, so you know when someone gets in touch if they were affected
- Invoicing events
- Order refunds or delivery events
In this recipe we'll be creating a refund timeline entry as seen in the screenshot:
Getting started
Before we dive deep into making API requests we first need to build a custom timeline entry.
You can use the custom timeline entry playground to prototype an entry. In this example we'll use the "Refund processed" template.
The components that you can use in the custom timeline entry are documented in Plain UI Components and the CustomTimelineEntryComponent Union.
You'll need to replace the "customerId": "c_XXXXXXXXXXXXXXXXXXXXXXXXXX"
with a real customer ID, which you can get from the Support App or by creating one.
We’ll be using the upsertCustomTimelineEntry
mutation which allows to create a new custom timeline entry or update one if it exists.
This mutation requires the following permissions:
timeline:create
timeline:edit
Creating a custom timeline entry
Mutation
The GraphQL mutation is the following:
mutation upsertCustomTimelineEntry($input: UpsertCustomTimelineEntryInput!) {
upsertCustomTimelineEntry(input: $input) {
result
timelineEntry {
id
customerId
timestamp {
iso8601
}
entry {
... on CustomEntry {
title
components {
... on ComponentText {
__typename
text
textSize
textColor
}
... on ComponentSpacer {
__typename
spacerSize
}
... on ComponentDivider {
__typename
spacingSize
}
... on ComponentLinkButton {
__typename
url
label
}
}
}
}
actor {
... on MachineUserActor {
machineUser {
id
fullName
publicName
}
}
}
}
error {
message
type
code
fields {
field
message
type
}
}
}
}
The TimelineEntry Object and Custom Entry Object have more fields you can select, but in this recipe we're only selecting a few important ones.
Variables
Remember to replace c_XXXXXXXXXXXXXXXXXXXXXXXXXX
with an existing customer's id.
{
"input": {
"customerId": "c_XXXXXXXXXXXXXXXXXXXXXXXXXX",
"title": "Refund processed",
"components": [
{
"componentText": {
"text": "Invoice **#1301** was refunded to customer"
}
},
{
"componentSpacer": {
"spacerSize": "XS"
}
},
{
"componentText": {
"textSize": "L",
"textColor": "SUCCESS",
"text": "**+ $413.20**"
}
},
{
"componentDivider": {
"spacingSize": "M"
}
},
{
"componentLinkButton": {
"url": "https://stripe.com",
"label": "View in Stripe"
}
}
]
}
}
Response
{
"data": {
"upsertCustomTimelineEntry": {
"result": "CREATED",
"timelineEntry": {
"id": "t_01G9F4N117KW1VDPCFB8JSM5SF",
"customerId": "c_01G8JNBQMCJ46JTR7FEB68HTN0",
"timestamp": {
"iso8601": "2022-08-02T11:21:58.055Z"
},
"entry": {
"title": "Refund processed",
"externalId": null,
"components": [
{
"__typename": "ComponentText",
"text": "Invoice **#1301** was refunded to customer",
"textSize": null,
"textColor": null
},
{
"__typename": "ComponentSpacer",
"spacerSize": "XS"
},
{
"__typename": "ComponentText",
"text": "**+ $413.20**",
"textSize": "L",
"textColor": "SUCCESS"
},
{
"__typename": "ComponentDivider",
"spacingSize": "M"
},
{
"__typename": "ComponentLinkButton",
"url": "https://stripe.com",
"label": "View in Stripe"
}
]
},
"actor": {
"machineUser": {
"id": "mu_01G8WQ1WJSRXH5C2PD5NR9TNSD",
"fullName": "Order service (Custom timeline entry)",
"publicName": "Demo Inc. Orders"
}
}
},
"error": null
}
}
}
Navigating to the customer's timeline you should also see the custom timeline entry created:
If you have any problems, please get in touch with us by email on help@plain.com, and we will be happy to help.