NAV
shell python go

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.

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:

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

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

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:

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:

  1. Split on the , character
  2. Split each item by the = character to get a list of key/value pairs
  3. Prevent replay attacks:
    1. Take the item with the key t and parse its value as a UNIX timestamp (the number of seconds since January 1st 1970)
    2. Check the timestamp is less than 5 minutes old
  4. Concatenate the UNIX timestamp, the . character, and the raw request body
  5. Create an HMAC-SHA256 hash of that string, using your webhook signing key
  6. 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.