Introduction π
Our AI agent integrates with many popular customer support platforms out of the box, but if you have an in-house support tool or need more control, you can build your own integration using our RESTful HTTP API.
Our API is accessible at https://api.gradient-labs.ai
. There are client
libraries available for Python
and Go.
Request and response bodies are encoded as JSON, with timestamps encoded as RFC 3339 strings.
Authentication π
Requests must be authenticated by providing your API Key in the Authorization
header, in the form: Bearer <your api key>
.
curl https://api.gradient-labs.ai \
--header "Authorization: Bearer $api_key"
import os
from gradient_labs import Client
client = Client(os.environ["api_key"])
package main
import (
"log"
"os"
glabs "github.com/gradientlabs-ai/go-client"
)
func main() {
client, err := glabs.NewClient(
glabs.WithAPIKey(os.Getenv("api_key")),
)
if err != nil {
log.Fatal(err)
}
}
Roles
API Keys have one of the following roles, which limits their permissions according to their intended use.
Integration
used to send live conversations from your support platformManagement
used to configure your AI agent (e.g. in an Infrastructure-as-Code pipeline)
This separation of permissions is intended to stop an API Key leaked from a CI/CD environment being used to disrupt live conversations, or exfiltrate customer data.
The role each endpoint requires is documented inline below.
Idempotence π
Unless stated otherwise, all endpoints are idempotent and requests can be safely retried.
Knowledge π
The AI agent can make use of your help content to answer general customer questions.
Add or update a topic
Topics enable you to categorise your help articles into groups.
curl https://api.gradient-labs.ai/topics \
--request POST \
--header "Authorization: Bearer $api_key" \
--header 'Content-Type: application/json' \
--data @- \
<<BODY
{
"id": "topic-a1",
"parent_id": "topic-a",
"name": "Account Management",
"description": "How to manage your account in our amazing app",
"visibility": "public",
"status": "published",
"created": "2023-10-23T17:24:50.804952Z",
"updated": "2024-08-20T09:27:32.682495Z"
}
BODY
# Pending implementation.
conv, err := client.UpsertArticleTopic(ctx, glabs.UpsertArticleTopicParams{
ID: "topic-a-1",
ParentID: "topic-a",
Name: "Account management",
Description: "How to manage your account in our amazing app",
Visibility: glabs.VisibilityPublic,
PublicationStatus: glabs.PublicationStatusPublished,
Created: time.Now(),
LastEdited: time.Now(),
})
Body Parameters
Parameter | Type | Required | Description |
---|---|---|---|
id |
String | β | Unique identifier for this topic. Can be anything consisting of letters, numbers, or any of the following characters: _ - + = . |
parent_id |
String | Unique identifier for this topic's parent topic. Top-level topics will have no parent, but sub-topics should point back up to their parent topic. | |
title |
String | β | The title or heading for this topic (e.g. "Account Management"). |
description |
String | A description for this topic. | |
visibility |
String | β | Identifies who can see this topic (must be one of public , users , or internal ). |
status |
String | β | The publication status of this topic (must be one of draft or published ). |
data |
Any | Arbitrary metadata to attach to this topic. | |
created |
Timestamp | β | When this topic was first created. |
last_edited |
Timestamp | β | When this topic was last edited. |
Required Role
Integration
Add or update an article
Articles are one kind of document that the AI agent can work with.
curl https://api.gradient-labs.ai/articles \
--request POST \
--header "Authorization: Bearer $api_key" \
--header 'Content-Type: application/json' \
--data @- \
<<BODY
{
"id": "article-123",
"parent_id": "topic-a-1",
"name": "How to change your address",
"body": "...",
"visibility": "public",
"status": "published",
"created": "2023-10-23T17:24:50.804952Z",
"updated": "2024-08-20T09:27:32.682495Z"
}
BODY
# Pending implementation.
conv, err := client.ArticleUpsert(ctx, glabs.UpsertArticleTopicParams{
ID: "article-123",
TopicID: "topic-a-1",
Name: "How to change your address",
Description: "How to manage your account in our amazing app",
Body: "...",
Visibility: glabs.VisibilityPublic,
PublicationStatus: glabs.PublicationStatusPublished,
Created: time.Now()
LastEdited: time.Now()
})
Body Parameters
Parameter | Type | Required | Description |
---|---|---|---|
id |
String | β | Unique identifier for this article. Can be anything consisting of letters, numbers, or any of the following characters: _ - + = . |
title |
String | The title or heading for this article (e.g. "How to change your address"). | |
description |
String | A description for this article. | |
body |
String | β | The main contents of this article. |
topic_id |
String | The topic that this article sits under. | |
author_id |
String | Optionally identifies the article's author. | |
visibility |
String | β | Identifies who can see this article (must be one of public , users , or internal ). |
status |
String | β | The publication status of this article (must be one of draft or published ). The AI agent will not use draft articles. |
data |
Any | Arbitrary metadata to attach to this article. | |
created |
Timestamp | β | When this article was first created. |
last_edited |
Timestamp | β | When this article was last edited. |
Required Role
Integration
Conversations π¬
Conversations are the core way that your customers can interact with our AI agent.
We also send webhooks to let you know when our agent wants to reply to a customer, hand-off to a human agent, or bring the conversation to a close.
Start a conversation
To begin a conversation, make a POST
request to the /conversations
endpoint.
curl https://api.gradient-labs.ai/conversations \
--request POST \
--header "Authorization: Bearer $api_key" \
--header 'Content-Type: application/json' \
--data @- \
<<BODY
{
"id": "conversation-1234",
"customer_id": "user-1234",
"channel": "web",
"created": "2023-10-23T17:24:50.804952Z"
"metadata": {
"chat_entrypoint": "home-page"
}
}
BODY
conv = client.start_conversation(
conversation_id="conversation-1234",
customer_id="user-1234",
channel="web",
metadata={
"chat_entrypoint": "home-page",
},
)
conv, err := client.StartConversation(ctx, glabs.StartConversationParams{
ID: "conversation-1234",
CustomerID: "user-1234",
Channel: glabs.ChannelWeb,
Metadata: map[string]string{"chat_entrypoint": "home-page"},
})
{
"id": "conversation-1234",
"customer_id": "user-1234",
"channel": "web",
"metadata": {
"chat_entrypoint": "home-page"
},
"created": "2023-10-23T17:24:50.804952Z",
"updated": "2023-10-23T17:24:50.804952Z",
"status": "active"
}
Body Parameters
Parameter | Type | Required | Description |
---|---|---|---|
id |
String | β | Unique identifier for this conversation. Can be anything consisting of letters, numbers, or any of the following characters: _ - + = . Tip: use something meaningful to your business (e.g. a ticket number). |
customer_id |
String | β | Unique identifier for the customer. Used to build historical context of conversations the agent has had with this customer. |
channel |
String | β | Identifier for the way the customer is getting in touch: one of web or email . |
created |
Timestamp | The time at which the conversation started (defaults to the current time). | |
metadata |
Any | Arbitrary metadata to attach to this conversation. Will be passed along with webhooks so can be used as action parameters. | |
assignee_id |
String | Unique identifier for the human agent or bot that is assigned to the conversation. Used to render assignment changes in the web app. | |
assignee_type |
String | Identifies the type of assignee (must be one of Agent , Bot , or AI Agent ). If the conversation is started with an assignee type of AI Agent , then our AI Agent will wait a configurable amount of time for the customer to start sending messages, and close out (finish) the chat if nothing happens. If the type is any of the other options, or omitted, then our AI Agent will remain in an 'observing' state until the conversation is assigned to it. |
Required Role
Integration
Add a message
To record a message sent by the customer or a human agent, make a POST
request
to the /conversations/:conversation_id/messages
endpoint.
curl "https://api.gradient-labs.ai/conversations/$conversation_id/messages" \
--request POST \
--header "Authorization: Bearer $api_key" \
--header 'Content-Type: application/json' \
--data @- \
<<BODY
{
"id": "message-1234",
"body": "Hello! I need some help setting up my toaster oven, please.",
"participant_id": "user-1234",
"participant_type": "Customer",
"created": "2023-10-23T17:24:50.804952Z",
"metadata": {
"device_os": "iOS 17"
}
}
BODY
from datetime import datetime
from gradient_labs import ParticipantType
msg = client.add_message(
conversation_id="conversation-1234",
message_id="message-1234",
body="Hello! I need some help setting up my toaster oven, please.",
participant_id="user-1234",
participant_type=ParticipantType.CUSTOMER,
metadata={
"device_os": "iOS 17",
},
attachments=[
Attachment(
type=AttachmentType.IMAGE,
file_name="toaster.jpg",
)
],
)
msg, err := client.AddMessage(ctx, "conversation-1234", glabs.AddMessageParams{
ID: "message-1234",
Body: "Hello! I need some help setting up my toaster oven, please.",
ParticipantID: "user-1234",
ParticipantType: glabs.ParticipantTypeCustomer,
Metadata: map[string]string{"device_os": "iOS 17"},
Attachments: []*glabs.Attachment{
{
Type: glabs.AttachmentTypeImage,
FileName: "toaster.jpg",
},
},
})
{
"id": "message-1234",
"body": "Hello! I need some help setting up my toaster oven, please.",
"participant_id": "user-1234",
"participant_type": "Customer",
"created": "2023-10-23T17:24:50.804952Z",
"metadata": {
"device_os": "iOS 17"
}
}
Body Parameters
Parameter | Type | Required | Description |
---|---|---|---|
id |
String | β | Unique identifier for this message. Can be anything consisting of letters, numbers, or any of the following characters: _ - + = . Tip: use something meaningful to your business. |
body |
String | The message text. Required if there are no attachments. | |
attachments |
List | An optional list of attachments that were sent with the message. Currently, our AI agent does not read attachments and will therefore hand-off any conversation where an attachment is uploaded. | |
participant_id |
String | β | Identifies the message sender. |
participant_type |
String | β | Identifies the type of sender (must be one of Customer , Agent , or Bot ). |
created |
Timestamp | The time at which the message was sent (defaults to the current time). | |
metadata |
Any | Arbitrary metadata to attach to this message. |
Required Role
Integration
Add an event
We support logging the following conversation events:
typing
when a participant is typingdelivered
when a message has been delivered to a participantread
when a participant has read a messagejoin
andleave
for participants joining and leaving the chat
To record an event, make a POST
request to the /conversations/:conversation_id/events
endpoint.
curl "https://api.gradient-labs.ai/conversations/$conversation_id/events" \
--request POST \
--header "Authorization: Bearer $api_key" \
--header 'Content-Type: application/json' \
--data @- \
<<BODY
{
"type": "typing",
"participant_id": "user-1234",
"participant_type": "Customer",
"timestamp": "2023-10-23T17:24:50.804952Z",
}
BODY
msg, err := client.AddConversationEvent(ctx, "conversation-1234", glabs.EventParams{
Type: glabs.ConversationEventTypeTyping,
ParticipantID: "user-1234",
ParticipantType: glabs.ParticipantTypeCustomer,
})
Body Parameters
Parameter | Type | Required | Description |
---|---|---|---|
type |
String | β | Type of event. One of typing , delivered , read , join , or leave. |
participant_id |
String | β | Identifies the participant. |
participant_type |
String | β | Identifies the type of participant (must be one of Customer , Agent , or Bot ). |
message_id |
String | For events that are message-specific (e.g. "read") optionally identifies the message. | |
timestamp |
Timestamp | The time at which the event was created (defaults to the current time). | |
idempotency_key |
String | Key to make the request idempotent. |
Required Role
Integration
Assign the conversation
Assignment controls who is currently responsible for responding to the customer, it's how you activate and deactivate the AI agent.
To assign the conversation, make a PUT
request to the
/conversations/:conversation_id/assignee
endpoint.
To assign the conversation to the AI agent, use the assignee type "AI Agent"
.
Note: This will fail if you have not configured a webhook endpoint that we
can use to deliver replies to the customer.
If you need to stop the AI agent from responding in a conversation (e.g. because a human agent has taken over), assign the conversation away from the AI agent by using one of the other available assignee types.
By default, conversations will start off without an assignment, unless you
pass the assignee_id
and assignee_type
parameters to the
start endpoint.
A conversation cannot be assigned to a customer.
curl "https://api.gradient-labs.ai/conversations/$conversation_id/assignee" \
--request PUT \
--header "Authorization: Bearer $api_key" \
--header 'Content-Type: application/json' \
--data @- \
<<BODY
{
"assignee_id": "agent-1234",
"assignee_type": "AI Agent",
"timestamp": "2023-10-23T17:24:50.804952Z",
}
BODY
from datetime import datetime
from gradient_labs import ParticipantType
client.assign_conversation(
conversation_id=conv.id,
participant_type=ParticipantType.AI_AGENT,
)
err = client.AssignConversation(ctx, conv.ID, &glabs.AssignmentParams{
AssigneeType: glabs.ParticipantTypeAIAgent,
})
if err != nil {
return err
}
{
"assignee_id": "agent-1234",
"assignee_type": "AI Agent",
}
Body Parameters
Parameter | Type | Required | Description |
---|---|---|---|
assignee_id |
String | Unique identifier for the human agent or bot that is assigned to the conversation. Used to render assignment changes in the web app. | |
assignee_type |
String | β | Identifies the type of assignee (must be one of Agent , Bot , or AI Agent ). |
timestamp |
Timestamp | The time at which the conversation was assigned (defaults to the current time). | |
reason |
String | Optionally allows you to add a description of why the assignment is happening. |
Required Role
Integration
Read a conversation
To retrieve the latest status of a conversation, make a GET
request to the /conversations/:conversation_id
endpoint.
curl "https://api.gradient-labs.ai/conversations/$conversation_id" \
--request GET \
--header "Authorization: Bearer $api_key"
client.read_conversation(conversation_id="conversation-1234")
err := client.ReadConversation(ctx, "conversation-1234")
Required Role
Integration
Finish a conversation
When the conversation is finished (e.g., because the customer issue has been resolved, or the chat is being closed after the customer has abandoned it), make a PUT
request to the /conversations/:conversation_id/finish
endpoint.
curl "https://api.gradient-labs.ai/conversations/$conversation_id/finish" \
--request PUT \
--header "Authorization: Bearer $api_key"
--data @- \
<<BODY
{
"timestamp": "2023-10-23T17:24:50.804952Z",
}
BODY
client.end_conversation(conversation_id="conversation-1234")
err := client.FinishConversation(ctx, "conversation-1234", glabs.FinishParams{})
Body Parameters
Parameter | Type | Required | Description |
---|---|---|---|
timestamp |
Timestamp | The time at which the conversation finished (defaults to the current time). | |
reason |
String | Optionally allows you to add description of why the conversation is finished. |
Cancel a conversation
Cancelling a conversation is different from finishing one. The APIs above are better suited to majority of cases: * If the conversation has reached a natural end state, use the finish API * If the conversation is being taken over by a human agent, use the assignment API
Cancellation enables you to permanently take the conversation away from Gradient Labs. For example, if your risk model determines the customer must be redirected to a human agent and the conversation must no longer be relayed on.
To cancel a conversation, make a PUT
request to the/conversations/%s/cancel
endpoint.
curl "https://api.gradient-labs.ai/conversations/$conversation_id/cancel" \
--request PUT \
--header "Authorization: Bearer $api_key"
--data @- \
<<BODY
{
"timestamp": "2023-10-23T17:24:50.804952Z",
}
BODY
#Β Pending implementation
err := client.CancelConversation(ctx, "conversation-1234", glabs.CancelParams{})
Body Parameters
Parameter | Type | Required | Description |
---|---|---|---|
timestamp |
Timestamp | The time at which the conversation was ended (defaults to the current time). | |
reason |
String | Optionally allows you to add description of why the conversation was cancelled. | |
reason |
String | Optionally allows you to add a reason string that describes why the conversation was cancelled. |
Required Role
Integration
Resources π¦
Attaching resources to a conversation gives our AI agent the context it needs to help customers with their specific, individual circumstances. Examples include: order details, recent transactions, or the customer's user profile.
A resource can be any JSON document, as long it is smaller than 1MB. There are no strict requirements on the format/structure of the document, but we recommend making attribute names as descriptive as possible.
Over time, the AI agent will learn the structure of your resources - so while it's fine to add new attributes, you may want to consider using new resource names when removing attributes or changing the structure of your resources significantly.
Resource names are case-insensitive and can be anything consisting of letters,
numbers, or any of the following characters: _
-
+
=
. Names should be
descriptive handles that are the same for all conversations (e.g. "order-details"
and "user-profile"
) rather than unique identifiers.
Resources can be "pushed" directly to our API, or "pulled" from an HTTP endpoint you provide or your webhook endpoint. This configuration, together with our inferred structure, is called the "resource type".
Push a resource
To push a resource to a conversation, make a PUT
request to the
/conversations/:conversation_id/resources/:name
endpoint. We'll automatically
create a resource type with the given name if it doesn't already exist.
curl "https://api.gradient-labs.ai/conversations/$conversation_id/resources/$name" \
--request PUT \
--header "Authorization: Bearer $api_key" \
--header 'Content-Type: application/json' \
--data @- \
<<BODY
{
"id": "1234",
"status": "shipped"
}
BODY
err = client.AddResource(ctx, conv.ID, "order-details", struct {
ID string `json:"id"`
Status string `json:"status"`
}{
ID: "1234",
Status: "shipped",
})
if err != nil {
return err
}
client.add_resource(
conversation_id=conv.id,
name="order-details",
data={
"id": "1234",
"status": "shipped",
},
)
Required Role
Integration
Create or update a resource type
To manually create or update a resource type, make a PUT
request to the
/resource-types/:name
endpoint.
curl "https://api.gradient-labs.ai/resource-types/$name" \
--request PUT \
--header "Authorization: Bearer $api_key" \
--header 'Content-Type: application/json' \
--data @- \
<<BODY
{
"display_name": "User Profile",
"description": "Includes the user's name, preferences, etc.",
"mode": "pull",
"pull_config": {
"http": {
"method": "GET",
"url_template": "https://my-api.com/users/${conversation.customer_id}",
"header_templates": {
"Authorization": "Bearer ${secrets.api_key}"
}
},
"cache": "10m"
}
}
BODY
Body Parameters
Parameter | Type | Required | Description |
---|---|---|---|
display_name |
String | Human-friendly name for the resource type that will be displayed in the web app. | |
description |
String | Contextual information about the data this resource type contains. | |
mode |
String | β | How this resource gets added to conversations (must be one of: push or pull ). |
pull_config |
Object | if mode is pull |
Controls how this resource will be pulled (see below). |
Pull Configuration
Resources can be pulled by either sending a resource.pull
event
to your configured webhook endpoint, or by making an HTTP request to the
endpoint of your choice.
Webhook pull resources allow you to use the existing signature-based authentication baked into our client libraries rather than managing your own tokens. Signatures make our requests to your system tamper-proof and resilient to replay attacks.
Using HTTP pull resources allows you to integrate the agent with existing APIs, but you must implement your own authentication and share credentials with us via secrets.
Parameter | Type | Description |
---|---|---|
webhook |
Object | Configuration for pulling this resource via webhooks. |
http |
Object | Configuration for pulling this resource via an HTTP request. |
cache |
String | How long the pulled data will be used before being refreshed. Must be a Go duration string, or never if the data is valid indefinitely. Default is 10m . |
Webhook Configuration
To pull resources via webhooks, set pull_config.webhook
to
an empty object. In the future this object will contain further configuration.
HTTP Configuration
Parameter | Type | Required | Description |
---|---|---|---|
http.method |
String | β | The HTTP method that will be used (e.g. GET or POST ). |
http.url_template |
String | β | The template that will be evaluated to get the pull URL. |
http.header_templates |
Map<String, String> | Map of templates that will be evaluated to get the request headers. | |
http.body |
Object | unless http.method is GET |
The request body configuration. |
http.body.encoding |
String | β | The body Content-Type . Must be application/json or application/x-www-form-urlencoded |
http.body.json_template |
String | if http.body.encoding is application/json |
The template that will be evaluated to get the request body. |
http.body.form_field_templates |
Map<String, String> | if http.body.encoding is application/x-www-form-urlencoded |
Map of templates that will be evaluated to get the form fields for the request body. |
Templates
Templates allow you to include dynamic data and secrets in your
URLs, headers, and request bodies. To interpolate to a variable, wrap it in
${}
delimiters (e.g. Hey there, ${conversation.metadata.first_name}
).
Available variables
- Secrets are exposed as
${secrets.your_secret_name}
${conversation.id}
${conversation.customer_id}
- Simple top-level fields (e.g. strings, numbers, booleans) in the conversation
metadata are exposed as
${conversation.metadata.your_field}
Required Role
Management
List resource types
To list your configured resource types (and the ones we've created
automatically), make a GET
request to the /resource-types
endpoint.
curl "https://api.gradient-labs.ai/resource-types" \
--request GET \
--header "Authorization: Bearer $api_key" \
{
"resource_types": [
{
"name": "user-profile",
"display_name": "User Profile",
"description": "Includes the user's name, preferences, etc.",
"mode": "pull"
}
]
}
Required Role
Management
Read a resource type
To read a resource type, including its "pull configuration"
make a GET
request to the /resource-types/:name
endpoint.
curl "https://api.gradient-labs.ai/resource-types/$name" \
--request GET \
--header "Authorization: Bearer $api_key" \
{
"name": "user-profile",
"display_name": "User Profile",
"description": "Includes the user's name, preferences, etc.",
"mode": "pull",
"pull_config": {
"http": {
"method": "GET",
"url_template": "https://my-api.com/users/${conversation.customer_id}",
"header_templates": {
"Authorization": "Bearer ${secrets.api_key}"
}
},
"cache": "10m"
}
}
Required Role
Management
Delete a resource type
To delete a resource type, make a DELETE
request to the
/resource-types/:name
endpoint.
curl "https://api.gradient-labs.ai/resource-types/$name" \
--request DELETE \
--header "Authorization: Bearer $api_key" \
Required Role
Management
Secrets π€«
Store sensitive data such as API credentials as secrets, and the agent can use them in tools and to pull resources. Secrets will be encrypted at rest using AES-256 and access to them is strictly audited.
Create or update a secret
To store a secret, make a PUT
request to the /secrets/:name
endpoint.
curl "https://api.gradient-labs.ai/secrets/$name" \
--request PUT \
--header "Authorization: Bearer $api_key" \
--header 'Content-Type: application/json' \
--data @- \
<<BODY
{
"value": "s3cret s4uce"
}
BODY
Required Role
Management
List secrets
To list your configured secrets, make a GET
request to the /secrets
endpoint.
curl "https://api.gradient-labs.ai/secrets" \
--request GET \
--header "Authorization: Bearer $api_key" \
{
"secrets": [
{
"name": "api_key",
"created": "2024-11-08T15:11:26.258766Z",
"updated": "2024-11-08T15:11:26.258766Z"
}
]
}
Required Role
Management
Revoke a secret
To permanently revoke a secret, make a DELETE
request to the /secrets/:name
endpoint.
curl "https://api.gradient-labs.ai/secrets/$name" \
--request DELETE \
--header "Authorization: Bearer $api_key" \
Required Role
Management
Tools π οΈ
Tools enable the agent to take action as part of assisting your customers. For example: generating a bank statement, or searching for a specific transaction.
Like pull-based resources, tools can deliver an event to your configured webhook endpoint, or make requests to an arbitrary HTTP endpoint of your choice.
Create a tool
To create a tool, make a POST
request to the /tools
endpoint.
curl "https://api.gradient-labs.ai/tools" \
--request POST \
--header "Authorization: Bearer $api_key" \
--header 'Content-Type: application/json' \
--data @- \
<<BODY
{
"name": "Launch the Rocket",
"description": "Send the rocket into the stratosphere",
"parameters": [
{
"name": "speed",
"description": "How fast the rocket will be launched",
"type": "string",
"required": true,
"options": [{"value": "slow", "text": "Slow"}, {"value": "medium", "text": "Medium"}, {"value": "warp-speed", "text": "Warp Speed"}]e
}
],
"webhook": {
"name": "rocket.launch"
}
}
BODY
required := true
tool, err := client.CreateTool(ctx, &glabs.Tool{
Name: "Launch the rocket",
Description: "Send the rocket into the stratosphere",
Parameters: []glabs.ToolParameter{
{
Name: "speed",
Description: "How fast the rocket will be launched",
Type: glabs.ParameterTypeString,
Required: &required,
Options: []glabs.ParameterOption{
{Value: "slow", Text: "Slow"},
{Value: "medium", Text: "Medium"},
{Value: "warp-speed", Text: "Warp Speed"},
},
},
},
Webhook: &glabs.ToolWebhookConfiguration{
Name: "rocket.launch",
},
})
Body Parameters
Parameter | Type | Required | Description |
---|---|---|---|
name |
String | β | Human-friendly name of the tool. |
description |
String | β | Brief explanation of what the tool does and when it should be used. |
parameters |
Array | Details of the parameters this tool accepts. | |
webhook |
Object | Configuration for executing this tool via an action.execute webhook event. |
|
http |
Object | Configuration for executing this tool via an arbitrary HTTP request. | |
mock |
Boolean | Marks the tool as a "mock" so it cannot be used in live conversations. |
Tool Parameter Configuration
Parameter | Type | Required | Description |
---|---|---|---|
name |
String | β | Name of the tool parameter. |
description |
String | β | Brief explanation of what the parameter is used for. |
type |
String | β | Type of data the parameter contains. Currently only string is supported. |
required |
Boolean | Whether the tool parameter is required. Currently only required parameters are supported. | |
options |
Array<Object> | List of options for the parameter. Each option must have a value and text field. |
Webhook Configuration
Set the webhook
parameter to configure the agent to execute the tool by
delivering an action.execute
event to your webhook endpoint.
Parameter | Type | Required | Description |
---|---|---|---|
webhook.name |
String | β | Will be passed along in the action field of the webhook payload, so you can tell which tool is being used. |
HTTP Configuration
Set the http
parameter to configure the agent to execute the tool by
making an HTTP request to the endpoint of your choice.
Parameter | Type | Required | Description |
---|---|---|---|
http.method |
String | β | The HTTP method that will be used (e.g. GET or POST ). |
http.url_template |
String | β | The template that will be evaluated to get the URL. |
http.header_templates |
Map<String, String> | Map of templates that will be evaluated to get the request headers. | |
http.body |
Object | unless http.method is GET |
The request body configuration. |
http.body.encoding |
String | β | The body Content-Type . Must be application/json or application/x-www-form-urlencoded |
http.body.json_template |
String | if http.body.encoding is application/json |
The template that will be evaluated to get the request body. |
http.body.form_field_templates |
Map<String, String> | if http.body.encoding is application/x-www-form-urlencoded |
Map of templates that will be evaluated to get the form fields for the request body. |
Templates
Templates allow you to include dynamic data and secrets in your
URLs, headers, and request bodies. To interpolate to a variable, wrap it in
${}
delimiters (e.g. Hey there, ${params.first_name}
).
Available variables
- Parameters are exposed as
${params.your_param_name}
- Secrets are exposed as
${secrets.your_secret_name}
${conversation.id}
${conversation.customer_id}
- Simple top-level fields (e.g. strings, numbers, booleans) in the conversation
metadata are exposed as
${conversation.metadata.your_field}
Required Role
Management
Read a tool
To read a tool, make a GET
request to the /tools/:id
endpoint.
curl "https://api.gradient-labs.ai/tools/$id" \
--request GET \
--header "Authorization: Bearer $api_key" \
{
"id": "act_01jf0fr976fscbhxadr3n9da1x",
"name": "Launch the Rocket",
"description": "Send the rocket into the stratosphere",
"parameters": [
{
"name": "speed",
"description": "How fast the rocket will be launched",
"type": "string",
"required": true,
"options": ["slow", "medium", "warp-speed"]
}
],
"webhook": {
"name": "rocket.launch"
}
}
rsp, err := client.ReadTool(ctx, tool.ID)
if err != nil {
return err
}
Required Role
Management
Update a tool
To update a tool, make a PUT
request to the /tools/:id
endpoint.
curl "https://api.gradient-labs.ai/tools/$tool_id" \
--request PUT \
--header "Authorization: Bearer $api_key" \
--header 'Content-Type: application/json' \
--data @- \
<<BODY
{
"name": "Launch the Rocket",
"description": "Send the rocket into the stratosphere",
"parameters": [
{
"name": "speed",
"description": "How fast the rocket will be launched",
"type": "string",
"required": true,
"options": ["slow", "medium", "warp-speed"]
}
],
"webhook": {
"name": "rocket.launch"
}
}
BODY
updatedTool, err := client.UpdateTool(ctx, &glabs.Tool{
ID: tool.ID,
Name: "Launch the rocket",
Description: "Send the rocket into the stratosphere",
Parameters: []glabs.ToolParameter{
{
Name: "speed",
Description: "How fast the rocket will be launched",
Type: glabs.ParameterTypeString,
Required: &required,
Options: []string{"slow", "medium", "fast", "warp-speed"},
},
},
Webhook: &glabs.ToolWebhookConfiguration{
Name: "rocket.launch",
},
})
Body Parameters
See the create tool endpoint's body parameters.
Required Role
Management
Delete a tool
To delete a tool, make a DELETE
request to the /tools/:id
endpoint. This
will not interrupt any in-flight conversations that are currently using the
tool.
curl "https://api.gradient-labs.ai/tools/$tool_id" \
--request DELETE \
--header "Authorization: Bearer $api_key"
err := client.UninstallTool(ctx, tool.ID)
Required Role
Management
List tools
To list your configured tools, make a GET
request to the /tools
endpoint.
curl "https://api.gradient-labs.ai/tools" \
--request GET \
--header "Authorization: Bearer $api_key" \
{
"tools": [
{
"id": "act_01jf0fr976fscbhxadr3n9da1x",
"name": "Launch the Rocket",
"description": "Send the rocket into the stratosphere",
"parameters": [
{
"name": "speed",
"description": "How fast the rocket will be launched",
"type": "string",
"required": true,
"options": ["slow", "medium", "warp-speed"]
}
],
"webhook": {
"name": "rocket.launch"
}
}
]
}
Required Role
Management
Hand-off Targets π―
Conversations can be escalated to human agents or other systems for further assistance.
Add or update a hand-off target
You can add or update hand-off targets. You can then reference them in your intents and procedures through the web app.
curl https://api.gradient-labs.ai/hand-off-targets \
--request POST \
--header "Authorization: Bearer $api_key" \
--header 'Content-Type: application/json' \
--data @- \
<<BODY
{
"id": "accounts",
"name": "Accounts",
}
BODY
client.upsert_hand_off_target(hand_off_target_id="accounts", name="Accounts")
conv, err := client.UpsertHandOffTarget(ctx, glabs.UpsertHandOffTargetParams{
ID: "accounts",
Name: "Account",
})
Body Parameters
Parameter | Type | Required | Description |
---|---|---|---|
id |
String | β | Unique identifier for this hand-off target, it will be sent back to you in the conversation.hand_off webhook event. Can be anything consisting of letters, numbers, or any of the following characters: _ - + = . |
name |
String | β | The human-friendly name of this hand-off target. |
Required Role
Management
List hand-off targets
To read all of your hand-off targets.
curl https://api.gradient-labs.ai/hand-off-targets \
--request GET \
--header "Authorization: Bearer $api_key" \
--header 'Content-Type: application/json' \
#Β Coming soon
rsp, err := client.ListHandOffTargets(ctx)
Required Role
Management
Webhooks πͺ
We deliver webhooks by making an HTTP POST
request to your configured endpoint.
Our webhooks will be delivered from IP addresses in the following ranges:
35.234.150.140/32
You can get also get this list programmatically using the /ip-addresses
endpoint to use in firewall
maintenance scripts.
Request format
PUT /your-endpoint HTTP/1.1
Host: your-api.com
Content-Type: application/json
User-Agent: GradientLabs/1.0
X-GradientLabs-Signature: t=1698953919,v1=9f16975886154e02fb0bb34694baf87b83d385d7941310c55d2021e3f5cdf780
{
"id": "webhook_01he8rwyp4fn3v870d925kz2nf",
"type": "agent.message",
"sequence_number": 5,
"timestamp": "2023-10-23T17:24:50.804952Z",
"data": {
"conversation": {
"id": "conversation-1234",
"customer_id": "user-1234",
"metadata": {
"chat_entrypoint": "home-page"
},
"body": "Sure, I can help you set up your toaster oven!"
}
}
}
The request body is a JSON object with the following structure.
Field | Type | Description |
---|---|---|
id |
String | Unique identifier for this event. |
type |
String | Type of event (documented below). |
sequence_number |
Integer | Sequential index of this event within the conversation (see below). |
timestamp |
Timestamp | Time at which this event was generated. |
data |
Object | Event data. |
The data
object for all conversation-related webhooks contains a
conversation
object with the following fields.
Field | Type | Description |
---|---|---|
id |
String | Your chosen unique identifier for this conversation. |
customer_id |
String | Your chosen identifier for the customer. |
metadata |
Any | Metadata you attached to the conversation. |
Sequence numbers
Each of the webhooks about a particular conversation includes an incrementing
number called the sequence_number
, which can be used to establish a
total order of events.
You can use this number to detect when events are missed or delivered in the
wrong order (e.g. to prevent sending a message with sequence_number = 5
before
an earlier message with sequence_number = 4
).
Signature verification
import os
from flask import Flask, request
from gradient_labs import Webhook, SignatureVerificationError
app = Flask(__name__)
signing_key = os.environ["webhook_signing_key"]
@app.route("/webhook", methods=["POST"])
def handle_webhook():
payload = request.data.decode("utf-8")
signature_header = request.headers.get("X-GradientLabs-Signature", None)
try:
event = Webhook.parse_event(
payload=payload,
signature_header=signature_header,
signing_key=signing_key,
)
except SignatureVerificationError:
print("Invalid signature!")
return "Bad signature", 401
print(
"Received event: id={id}, type={type}".format(
id=event.event_id, type=event.event_type
)
)
return "", 200
if __name__ == "__main__":
app.run(port=int(os.environ.get("PORT", 5000)))
package main
import (
"errors"
"log"
"net/http"
"os"
glabs "github.com/gradientlabs-ai/go-client"
)
func main() {
client, err := glabs.NewClient(
glabs.WithAPIKey(os.Getenv("api_key")),
glabs.WithWebhookSigningKey(os.Getenv("webhook_signing_key")),
)
if err != nil {
log.Fatal(err)
}
http.ListenAndServe(":4321", webhookHandler(client))
}
func webhookHandler(client *glabs.Client) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
webhook, err := client.ParseWebhook(r)
switch {
case errors.Is(err, glabs.ErrInvalidWebhookSignature):
w.WriteHeader(http.StatusUnauthorized)
return
case errors.Is(err, glabs.ErrUnknownWebhookType):
log.Printf("unknown webhook type: %q", webhook.Type)
return
case err != nil:
w.WriteHeader(http.StatusInternalServerError)
log.Printf("failed to parse webhook: %v", err)
return
}
// Handle the webhook...
})
}
We sign webhooks using HMAC-SHA256, so you can tell they're really from us.
This signature is in the X-GradientLabs-Signature
request header. Our client
libraries can check it for you, but to do it yourself:
- Split on the
,
character - Split each item by the
=
character to get a list of key/value pairs - Prevent replay attacks:
- Take the item with the key
t
and parse its value as a UNIX timestamp (the number of seconds since January 1st 1970) - Check the timestamp is less than 5 minutes old
- Take the item with the key
- Concatenate the UNIX timestamp, the
.
character, and the raw request body - Create an HMAC-SHA256 hash of that string, using your webhook signing key
- Hex-encode the hash and compare it to each item with the key
v1
, if one of them is a match, the request is legitimate! π
Failure, timeouts, and retries
If your endpoint does not respond with a 2XX
status code within 10 seconds,
we'll consider webhook delivery failed, and begin retrying with exponential
back-off and jitter.
Critical webhooks (agent.message
, conversation.hand_off
and
conversation.finished
) will be retried 36 times over a 24 hour period.
If delivery has still not succeeded, the agent will mark the conversation as
failed
and stop processing it.
Tool webhooks (action.execute
) will be retried for up to 1 minute, and if
they still have not succeeded, the conversation will be handed off to a human
agent.
agent.message
We send this event when the agent wants to send a message to the customer.
{
"id": "webhook_01he8rwyp4fn3v870d925kz2nf",
"type": "agent.message",
"sequence_number": 5,
"timestamp": "2023-10-23T17:24:50.804952Z",
"data": {
"conversation": {
"id": "conversation-1234",
"customer_id": "user-1234",
"metadata": {
"chat_entrypoint": "home-page"
}
},
"body": "Sure, I can help you set up your toaster oven!"
}
}
Payload Fields
Field | Type | Description |
---|---|---|
body |
String | Text of the message the agent wants to send to the customer. |
conversation.hand_off
{
"id": "webhook_01he8rwyp4fn3v870d925kz2nf",
"type": "conversation.hand_off",
"sequence_number": 5,
"timestamp": "2023-10-23T17:24:50.804952Z",
"data": {
"conversation": {
"id": "conversation-1234",
"customer_id": "user-1234",
"metadata": {
"chat_entrypoint": "home-page"
}
}
}
}
We send this event when the agent is escalating or handing the conversation off to a human agent.
conversation.finished
{
"id": "webhook_01he8rwyp4fn3v870d925kz2nf",
"type": "conversation.finished",
"sequence_number": 5,
"timestamp": "2023-10-23T17:24:50.804952Z",
"data": {
"conversation": {
"id": "conversation-1234",
"customer_id": "user-1234",
"metadata": {
"chat_entrypoint": "home-page"
}
}
}
}
We send this event when the agent has concluded the conversation with the customer and you should close any corresponding ticket, etc.
action.execute
We send this event when the agent wants to use one of your configured webhook-backed tools.
For data-retrieval tools, you should respond with a JSON object containing the data. Note: don't have to tell us the response schema up-front, the agent is able to extract data from any well-formed JSON object using AI. πͺ
{
"id": "webhook_01he8rwyp4fn3v870d925kz2nf",
"type": "action.execute",
"sequence_number": 5,
"timestamp": "2023-10-23T17:24:50.804952Z",
"data": {
"conversation": {
"id": "conversation-1234",
"customer_id": "user-1234",
"metadata": {
"chat_entrypoint": "home-page"
}
},
"action": "random-dog-fact",
"params": {
"breed": "Golden Retriever"
}
}
}
Payload Fields
Field | Type | Description |
---|---|---|
action |
String | Your given identifier for this action. |
params |
Object | Parameters the agent has generated for this action. |
resource.pull
We send this event when the agent wants to pull one of your configured
webhook-backed resources. Your response should be a JSON object
containing the data, with the Content-Type
header application/json
.
{
"id": "webhook_01he8rwyp4fn3v870d925kz2nf",
"type": "resource.pull",
"sequence_number": 5,
"timestamp": "2023-10-23T17:24:50.804952Z",
"data": {
"conversation": {
"id": "conversation-1234",
"customer_id": "user-1234",
"metadata": {
"chat_entrypoint": "home-page"
}
},
"resource_type": "user-profile"
}
}
Payload Fields
Field | Type | Description |
---|---|---|
resource_type |
String | Name of the resource type the agent wants to pull. |