Introduction
For users who would like to integrate Saleswhale with a non-Salesforce CRM, or configure a custom workflow between Saleswhale and your existing systems, you can do so with our API.
The Saleswhale API allows you to:
- programmatically create Leads and initiate Conversations with them via enrolment in a Campaign
There is also webhook available. It allows you to:
- be notified of and handle Saleswhale events e.g. Conversations reaching a Terminal State
Authentication
Getting an API Key
To access the API, you will need an API Key. The Key can be found in the Saleswhale App under Settings.
Authentication Methods
The API Key should be included in each request, either as a query parameter or as a property in the JSON body of the request.
Query Parameter
curl https://engage-api.saleswhale.com/api/{version}/{api_endpoint}/?token=<API_KEY>
In the URL of each request, include a token
query parameter, with your API key as the value.
Request Body
curl https://engage-api.saleswhale.com/api/{version}/{api_endpoint} -X POST -H "Content-Type: application/json" -d "{\"token\":\"<API_KEY>\"}"
In the JSON body of each request, include the token
property, with your API key as the value.
Errors
Example Response:
{
"errors": [
{
"id": "ffcf2b4c1076be36ba841535103503",
"message": "Lead 41552 does not exist.",
"status": "unprocessable_entity"
}
]
}
Our API returns standard HTTP success or error status codes. The various HTTP status codes we might return are listed below.
Code | Title | Description |
---|---|---|
200 | OK | The request was successful. |
201 | Created | The resource was successfully created. |
400 | Bad Request | Bad request e.g. missing properties. |
401 | Unauthorized | Your API key is invalid |
422 | Unprocessable Entity | Validation error e.g. duplicated record. |
429 | Too Many Requests | Rate limit is exceeded. Limit is 5 requests per second. |
500 | Internal Server Error | An error occured with our API. |
Returns
For most errors, we will also return a JSON object with more details. If you have any issues resolving the error, please contact our Customer Success Team at support@saleswhale.com, including the error object in your communications.
Pagination
Example Request:
GET https://engage-api.saleswhale.com/api/v2/conversations?page=2&per_page=5
Example Response:
{
"conversations": [
{ ... },
{ ... },
{ ... },
{ ... },
{ ... }
],
"meta": {
"current_page": 2,
"next_page": 3,
"prev_page": 1,
"total_pages": 4,
"total_count": 18
}
}
Pagination is supported by some routes in our API. These routes support the following query parameters:
Parameter | Type | Description |
---|---|---|
page | integer | (optional) Page to return. First page is 1 . |
per_page | integer | (optional) Maximum number of objects per page. |
In addition, paginated routes will also return a meta object as part of the response body. The meta object will contain the following properties:
Property | Type | Description |
---|---|---|
current_page | integer | Number of the current page. |
next_page | integer | Number of the next page. |
prev_page | integer | Number of the previous page. |
total_pages | integer | Total number of pages. |
total_count | integer | Total number of objects. |
Supported Routes
v2
Team
Team Object
Property | Type | Description |
---|---|---|
id | integer | The ID of the team. |
name | string | The name of the team. |
Retrieve Team
Example Request:
GET https://engage-api.saleswhale.com/api/v2/team
Example Response:
{
"team": {
"id": 91233,
"name": "The Best Team"
}
}
Returns
A Team object representing the authenticated team.
Leads
Create a Lead
Example Request:
POST https://engage-api.saleswhale.com/api/v2/leads
{
"lead": {
"first_name": "Myah",
"last_name": "Harber",
"email": "myah.harber@kihn.info",
"job_title": "Internal Marketing Manager",
"lead_owner": {
"id": 4421,
"type": "sales_rep"
},
"crm_object_id": "005UK00000118iRYAS",
"crm_object_type": "Contact",
"crm_type": "salesforce",
"account_name": "Kihn Pte. Ltd.",
"custom_attribute_values": [
{
"custom_attribute_id": 51662,
"value": "Email Marketing"
}
]
}
}
Example Response:
{
"lead": {
"id": 21248461,
"first_name": "Myah",
"last_name": "Harber",
"email": "myah.harber@kihn.info",
"job_title": "Internal Marketing Manager",
"follow_up_at": "2018-08-29T06:20:58.909Z",
"do_not_contact": false,
"lead_owner": {
"id": 4421,
"type": "sales_rep"
},
"crm_object_id": "005UK00000118iRYAS",
"crm_object_type": "Contact",
"crm_type": "salesforce",
"account_name": "Kihn Pte. Ltd.",
"custom_attribute_values": [
{
"custom_attribute_id": 51662,
"custom_attribute_name": "lead_source",
"value": "Email Marketing"
}
]
}
}
The lead is created in Saleswhale, but is not enrolled into any campaigns. No email is sent to the lead at this point.
After a new lead is created in Saleswhale, you can find the lead under the Leads tab.
To create a Lead, the request should contain the following JSON object:
Property | Type | Description | Required |
---|---|---|---|
lead | object | The Lead object | Yes |
The Lead object accepts the following properties:
Property | Type | Description | Required |
---|---|---|---|
first_name | string | The first name of your lead. | Yes |
last_name | string | The last name of your lead. | No |
string | The email of your lead. | Yes | |
job_title | string | The job title or role of your lead. | No |
lead_owner | object | The Lead Owner object. | No |
crm_object_id | string | The id of the crm_object that your lead is associated with. | No |
crm_object_type | string | The type of the crm_object that your lead is associated with. Valid values: "Contact", "Lead" | No |
crm_type | string | The type of the crm that the crm_object belongs to. Valid values: "salesforce" | No |
account_name | string | The name of the account or company that your lead belongs to. | No |
custom_attribute_values | array | An array of Custom Attribute Value objects to assign to the lead. | No |
Create a lead with a lead owner
To create a lead with a lead owner, please first make sure that the lead owner is defined as a handoff rep in the app. We will only accept lead owners who are pre-populated in the app as a handoff rep. Otherwise, the lead owner will be discarded and the lead will be handed over on a round robin basis in the event of a qualification.
The Lead Owner object accepts the following properties:
Property | Type | Description | Required |
---|---|---|---|
id | number | The ID of the Sales Rep, retrieved via the Sales Rep endpoint. | Yes |
type | string | The type of Lead Owner object (e.g. "sales_rep") | Yes |
Create a lead with custom attributes
To create a lead with custom attributes, please first make sure that the custom attribute is created in the app.
The Custom Attribute Value object accepts the following properties:
Property | Type | Description | Required |
---|---|---|---|
custom_attribute_id | number | The ID of the Custom Attribute, retrieved via the Custom Attribute endpoint. | Yes |
value | string | The value to assign to the Custom Attribute. | Yes |
Returns
A Lead object.
Update a Lead
Example Request:
PUT https://engage-api.saleswhale.com/api/v2/leads/{lead_id}
{
"lead": {
"first_name": "Myah",
"last_name": "Harber",
"email": "myah.harber@kihn.info",
"job_title": "Internal Marketing Manager",
"lead_owner": {
"id": 4421,
"type": "sales_rep"
},
"crm_object_id": "005UK00000118iRYAS",
"crm_object_type": "Contact",
"crm_type": "salesforce",
"account_name": "Kihn Pte. Ltd.",
"custom_attribute_values": [
{
"custom_attribute_id": 51662,
"value": "Email Marketing"
}
]
}
}
Example Response:
{
"lead": {
"id": 21248461,
"first_name": "Myah",
"last_name": "Harber",
"email": "myah.harber@kihn.info",
"job_title": "Internal Marketing Manager",
"follow_up_at": "2018-08-29T06:20:58.909Z",
"do_not_contact": false,
"lead_owner": {
"id": 4421,
"type": "sales_rep"
},
"crm_object_id": "005UK00000118iRYAS",
"crm_object_type": "Contact",
"crm_type": "salesforce",
"account_name": "Kihn Pte. Ltd.",
"custom_attribute_values": [
{
"custom_attribute_id": 51662,
"custom_attribute_name": "lead_source",
"value": "Email Marketing"
}
]
}
}
To update a Lead, the request may contain the following JSON object:
Property | Type | Description | Required |
---|---|---|---|
lead | object | The Lead object | Yes |
The Lead object accepts the following properties:
Property | Type | Description | Required |
---|---|---|---|
first_name | string | The first name of your lead. | No |
last_name | string | The last name of your lead. | No |
string | The email of your lead. | No | |
job_title | string | The job title or role of your lead. | No |
lead_owner | object | The Lead Owner object. | No |
crm_object_id | string | The id of the crm_object that your lead is associated with. | No |
crm_object_type | string | The type of the crm_object that your lead is associated with. Valid values: "Contact", "Lead" | No |
crm_type | string | The type of the crm that the crm_object belongs to. Valid values: "salesforce" | No |
account_name | string | The name of the account or company that your lead belongs to. | No |
custom_attribute_values | array | An array of Custom Attribute Value objects to assign to the lead. | No |
The Lead Owner object accepts the following properties:
Property | Type | Description | Required |
---|---|---|---|
id | number | The ID of the Sales Rep, retrieved via the Sales Rep endpoint. | Yes |
type | string | The type of Lead Owner object (e.g. "sales_rep") | Yes |
The Custom Attribute Value object accepts the following properties:
Property | Type | Description | Required |
---|---|---|---|
custom_attribute_id | number | The ID of the Custom Attribute, retrieved via the Custom Attribute endpoint. | Yes |
value | string | The value to assign to the Custom Attribute. | Yes |
Returns
A Lead object.
Delete a Lead
Example Request:
DELETE https://engage-api.saleswhale.com/api/v2/leads/{lead_id}
{
"force": false
}
Please take note that our delete endpoint is for soft delete. Soft delete archives the lead in the app, and the lead profile is no longer accessible. The conversation will still be present. If you create a lead with the same email, this archived lead will be restored and updated with the new values.
By default, the API prevents you from deleting a Lead that has an ongoing Conversation. You can override this behaviour (delete the Lead and cancel his/her ongoing Conversation) by including the force
property in the JSON body.
Property | Type | Description | Required |
---|---|---|---|
force | boolean | Force delete a Lead, canceling his/her ongoing Conversation. | No |
Hard delete a lead
If you are testing the import/enrollment endpoints and need to start from a clean slate, you will need to hard delete the lead.
Hard delete permanently deletes the lead from our database. All conversation and lead details will be irreversibly lost, and stats attributed to it will be reverted.
Hard delete is currently not available over API or within the app. To do so, please contact your customer success representative or open a support ticket with support [at] saleswhale.com. Alternatively, you can test the endpoints on the same email multiple times like this: example+1@company.com, example+2@company.com
Multiple leads in a conversation
If you delete a lead in a conversation with multiple leads:
- Soft-deleting a lead/all leads will not delete the conversation
- Hard deleting a lead will not delete the conversation, but hard deleting all leads will delete the conversation
Returns
A successful delete operation will return a 204 HTTP status with no object. Any errors will be returned as an object.
Retrieve a Lead
Example Request:
GET https://engage-api.saleswhale.com/api/v2/leads/{lead_id}
Example Response:
{
"lead": {
"id": 21248461,
"first_name": "Myah",
"last_name": "Harber",
"email": "myah.harber@kihn.info",
"job_title": "Internal Marketing Manager",
"follow_up_at": "2018-08-29T06:20:58.909Z",
"do_not_contact": false,
"lead_owner": {
"id": 4421,
"type": "sales_rep"
},
"crm_object_id": "005UK00000118iRYAS",
"crm_object_type": "Contact",
"crm_type": "salesforce",
"account_name": "Kihn Pte. Ltd.",
"custom_attribute_values": [
{
"custom_attribute_id": 51662,
"custom_attribute_name": "lead_source",
"value": "Email Marketing"
}
]
}
}
Returns
A Lead object.
Fetch conversations of Lead
Example Request:
GET https://engage-api.saleswhale.com/api/v2/leads/{lead_id}/conversations/
Example Response:
// Qualified Engagement
{
"conversation": {
"id": 12513522,
"started_at": "2018-08-16T06:32:00.000Z",
"handover_rep": {
"id": 95233,
"type": "sales_rep"
},
"leads": [
{
"id": 32244,
"email": "clara.smith@saleswhale.com",
"is_main": false,
"is_original": true
},
{
"id": 32552,
"email": "michael.wyke@saleswhale.com",
"is_main": true,
"is_original": false
}
],
"status": "qualified",
"enrolment": {
"id": 2214225,
"status": "processed",
"campaign_id": 2245,
"lead_id": 32244,
"errors": null,
"conversation_id": 12513522
}
}
}
Returns
Campaigns
A Campaign has a status
- the self-explanatory value shows the campaign's point in its lifecycle.
Status | Can Leads be enroled? |
---|---|
draft | no |
scheduled | yes |
running | yes |
ended | no |
canceled | no |
paused | no |
List all Campaigns
Example Request:
GET https://engage-api.saleswhale.com/api/v2/campaigns?status[]=scheduled&status[]=running
Example Response:
{
"campaigns": [
{
"id": 41222,
"name": "Scheduled Lead Engagement",
"status": "scheduled",
"updated_at": "2022-08-14T10:30:08.000Z"
},
{
"id": 41223,
"name": "Inbound Lead Engagement",
"status": "running",
"updated_at": "2022-08-14T10:30:08.000Z"
}
]
}
Query Parameters
Parameter | Type | Description |
---|---|---|
status[] | string | (optional) Only returns campaigns with the specified statuses. Valid statuses are scheduled , running , ended , canceled and paused . |
updated_at__is_after | string (ISO-8601 timestamp) | (optional) Only returns campaigns that are updated after this timestamp. |
Returns
An array of Campaign objects. draft
Campaigns are never returned.
Sales Reps
List all Sales Reps
Example Request:
GET https://engage-api.saleswhale.com/api/v2/sales_reps
GET https://engage-api.saleswhale.com/api/v2/sales_reps?email=jeramine@example.co
Query Parameters
Parameter | Type | Description |
---|---|---|
string | (optional) Only returns sales rep specified by the email. |
Example Response:
{
"sales_reps": [
{
"id": 91233,
"type": "sales_rep",
"email": "jermaine@example.co",
"first_name": "Jermaine",
"last_name": "Clement"
}
]
}
Returns
An array of Sales Rep objects.
Retrieve a Sales Rep
Example Request:
GET https://engage-api.saleswhale.com/api/v2/sales_reps/{sales_rep_id}
Example Response:
{
"sales_rep": {
"id": 5283,
"type": "sales_rep",
"email": "marlon@example.co",
"first_name": "Marlon",
"last_name": "Turcotte"
}
}
Returns
A Sales Rep object. Returns HTTP Error 422 if it doesn't exist.
Custom Attributes
List all Custom Attributes
Example Request:
GET https://engage-api.saleswhale.com/api/v2/custom_attributes
Example Response:
{
"custom_attributes": [
{
"id": 98221,
"name": "lead_source",
"fallback_value": "",
"attr_type": "lead"
}
]
}
Returns
An array of Custom Attribute objects.
Enrolments
Endpoints
GET /api/v2/enrolments
GET /api/v2/enrolments/{enrolment_id}
POST /api/v2/enrolments/{enrolment_id}/cancel
An Enrolment object represents the intent to enrol a specific Lead to a specific Campaign.
Enrolment Object
Property | Type | Description |
---|---|---|
id | integer | The ID of the enrolment. |
status | string | The status of the enrolment, either "processed" or "error" |
state | string | The state of the enrolment (supersedes "status") |
campaign_id | integer | The ID of the related Campaign. |
lead_id | integer | The ID of the related Lead. |
conversation_id | integer | The ID of the related Conversation, if it exists. |
errors | array | Any errors in the Enrolment. |
Enrolment State
An Enrolment can only have one state at any point in time.
A list of possible states can be found below:
State | Description |
---|---|
planned | The Enrolment is in the process of being created. |
pending | The Enrolment will be processed when the Campaign starts. |
scheduled | The Enrolment is in progress and will be processed or invalidated soon. |
processed | The Enrolment has been processed and a Conversation was created. |
invalidated | There were some errors that prevented the Enrolment from being processed. |
canceled | The Enrolment has been canceled. |
Create an Enrolment
Example Request:
POST https://engage-api.saleswhale.com/api/v2/enrolments
{
"enrolment": {
"lead_id": 155169,
"campaign_id": 12375
}
}
Example Response:
// Successful Enrolment
{
"enrolment": {
"id": 512562,
"state": "processed",
"status": "processed",
"campaign_id": 12375,
"lead_id": 155169,
"errors": null,
"conversation_id": 13512266 // can be `null` if the campaign is scheduled for a later date
}
}
// Errors in Enrolment
{
"enrolment": {
"id": 512563,
"state": "invalidated",
"status": "error",
"campaign_id": 12375,
"lead_id": 155169,
"errors": [
"Lead already engaged",
"Lead was in this campaign"
],
"conversation_id": null
}
}
To enrol a lead, you will need to:
- Make sure the lead is first created in the application under the 'Leads' tab or first created via the API. Then retrieve the
lead
id from the response of your lead creation API call - Make sure the campaign you wish to enrol to is first created in the app. You will need to retrieve all campaign, do a search and match on your end based on the campaign name to retrieve the campaign id. You can then submit this in with your POST request. Note that for a campaign that is scheduled for a later date, you can still create an enrolment for the lead but it will be processed only when the campaign goes live.
- There should be handover sales rep available.
- The AI Assistant should have been created/onboarded in the application.
If you're having problems getting started, please contact your customer success representative or open a support ticket with support@saleswhale.com.
To create an Enrolment to enrol a Lead into a Campaign, the request should contain the following JSON object:
Property | Type | Description | Required |
---|---|---|---|
enrolment | object | The Enrolment object | Yes |
The Enrolment object accepts the following properties:
Property | Type | Description | Required |
---|---|---|---|
campaign_id | string | The ID of the Campaign to enrol the Lead to. | Yes |
lead_id | string | The ID of the Lead to enrol into the Campaign. | Yes |
Returns
An Enrolment object.
Retrieve an Enrolment
Example Request:
GET https://engage-api.saleswhale.com/api/v2/enrolments/{enrolment_id}
Example Response:
{
"enrolment": {
"id": 512562,
"state": "processed",
"status": "processed",
"campaign_id": 12375,
"lead_id": 155169,
"errors": null,
"conversation_id": 13512266
}
}
Returns
An Enrolment object.
Cancel an Enrolment
Example Request:
POST https://engage-api.saleswhale.com/api/v2/enrolments/{enrolment_id}/cancel
Example Response:
{
"enrolment": {
"id": 512562,
"state": "canceled",
"status": "error",
"campaign_id": 12375,
"lead_id": 155169,
"errors": null,
"conversation_id": null
}
}
Returns
An Enrolment object.
Conversations
Endpoints
GET /api/v2/conversations
GET /api/v2/conversations/{conversation_id}
POST /api/v2/conversations/{conversation_id}/cancel
Conversation objects represent the engagement between your AI assistant and enrolled leads.
Conversation Object
Property | Type | Description |
---|---|---|
id | integer | The ID of the conversation. |
started_at | string | The time the conversation started. |
handover_rep | object | Details of the handover rep. |
leads | array | Details of all the leads and their relationship in the conversation. |
status | string | The status of the conversation. |
enrolment | object | Details of the enrolment. |
Leads
Information of all the leads involved and their relationship to the conversation. Returns an array of truncated lead objects with the following properties:
Property | Type | Description |
---|---|---|
id | integer | The ID of the lead. |
string | The email of the lead. | |
is_main | boolean | Main lead when the conversation is completed, which is assumed to be the last person who replied to the AI assistant. If marked as true, this lead is the 'To' recipient. If false, this lead is either a 'CC' or 'BCC' recipient. |
is_original | boolean | Original lead the AI assistant first started the conversation with. |
In a conversation where your lead refers the AI assistant to their colleague, or CCs in other people, there will be multiple leads in a conversation.
All leads within a conversation will have the same conversation status, as the status belongs to the conversation and not the individual leads.
Conversation Statuses
A conversation can only have one status at any point in time. Some are Terminal Statuses, where the AI assistant has stopped attempting engage the lead, and a new Conversation can be initiated with the lead.
A list of possible statuses can be found below:
Status | Is Terminal Statuses? | Description |
---|---|---|
pending_lead_response | No | The Conversation is ongoing - our AI assistant is awaiting a response from the lead. |
pending_bot_response | No | The Conversation is ongoing - the lead is awaiting a response from our AI assistant. |
human_review | No | The Conversation has gone out of scope of the Topic Template and human involvement is required. |
bounced | Yes | The Conversation could not be started because the Lead's email address is invalid. |
not_interested | Yes | The Lead has explicitly stated he or she is not interested in furthering the conversation. |
qualified | Yes | The sales and marketing goal of the Topic Template was achieved. |
stop_responding | Yes | The Lead replied at least once, but thereafter stopped responding. |
unresponsive | Yes | The Lead did not reply to the Main Sequence. |
referral_canceled | Yes | The Conversation was canceled as there are two conversations that involve the same lead. |
canceled | Yes | The Conversation was canceled. |
List Conversations
Example Request:
GET https://engage-api.saleswhale.com/api/v2/conversations?status[]=qualified&status[]=not_interested
Example Response:
{
"conversations": [
{
"id": 446461,
"started_at": "2018-08-16T03:40:00.000Z",
"handover_rep": {
"id": 1234,
"type": "sales_rep"
},
"leads": [
{
"id": 446461,
"email": "dyke.roman@saleswhale.com",
"is_main": true,
"is_original": true
}
],
"status": "qualified",
"enrolment": {
"id": 355253,
"status": "processed",
"campaign_id": 4145,
"lead_id": 446461,
"errors": null,
"conversation_id": 446461
}
},
{
"id": 446457,
"started_at": "2018-07-13T02:18:00.000Z",
"handover_rep": {
"id": 1234,
"type": "sales_rep"
},
"leads": [
{
"id": 443500,
"email": "jane.doe@saleswhale.com",
"is_main": true,
"is_original": true
}
],
"status": "qualified",
"enrolment": {
"id": 355173,
"status": "processed",
"campaign_id": 4145,
"lead_id": 443500,
"errors": null,
"conversation_id": 443500
}
}
],
"meta": {
"current_page": 1,
"next_page": null,
"prev_page": null,
"total_pages": 1,
"total_count": 2
}
}
Query Parameters
Parameter | Type | Description |
---|---|---|
status[] | string | (optional) Only returns conversations with the specified statuses. See Conversation for a list of valid statuses. |
Returns
An array of Conversation objects in order of decreasing id
.
Show a Conversation
Example Request:
GET https://engage-api.saleswhale.com/api/v2/conversations/{conversation_id}
Example Response:
// Not Interested Engagement
{
"conversation": {
"id": 12513522,
"started_at": "2018-08-16T06:32:00.000Z",
"handover_rep": {
"id": 95233,
"type": "sales_rep"
},
"leads": [
{
"id": 32244,
"email": "clara.smith@saleswhale.com",
"is_main": false,
"is_original": true
},
{
"id": 32552,
"email": "michael.wyke@saleswhale.com",
"is_main": true,
"is_original": false
}
],
"status": "not_interested",
"enrolment": {
"id": 2214225,
"status": "processed",
"campaign_id": 2245,
"lead_id": 32244,
"errors": null,
"conversation_id": 12513522
}
}
}
Returns
Cancel a Conversation
Example Request:
POST https://engage-api.saleswhale.com/api/v2/conversations/{conversation_id}/cancel
Example Response:
{
"conversation": {
"id": 446461,
"started_at": "2018-08-16T03:40:00.000Z",
"handover_rep": null,
"leads": [
{
"id": 446461,
"email": "dyke.roman@saleswhale.com",
"is_main": true,
"is_original": true
}
],
"status": "canceled",
"enrolment": {
"id": 355253,
"status": "processed",
"campaign_id": 4145,
"lead_id": 446461,
"errors": null,
"conversation_id": 446461
}
}
}
Returns
Webhook
Saleswhale can notify your application of events via a webhook. You can also retrieve resource information on-demand via the API endpoints available.
Setup
First, setup the webhook Target URL in your Account Settings
Receiving a Webhook
Checking Signature
# Computing the signatures for comparison
# Python 3
expected_signature = hmac.new(
"your_api_key".encode(),
f"{timestamp}.{body}".encode(),
digestmod=hashlib.sha256
).hexdigest()
# Ruby
expected_signature = OpenSSL::HMAC.hexdigest(
OpenSSL::Digest.new('sha256'),
your_api_key,
"#{timestamp}.#{body}"
)
We recommend you verify that each webhook request is from Saleswhale. Each Webhook request we send includes signature
and timestamp
query parameters for this purpose e.g. we would POST the webhook to <your_webhook_url>?signature=<signature>×tamp=<timestamp>
.
Steps to verify a Saleswhale signature:
- Compute a SHA256 HMAC hexdigest of your own using your Saleswhale API Key together with the timestamp parameter and the webhook request body. Be sure to add a period punctuation mark when concatenating the timestamp and body of the webhook request, and to use the raw body as is (e.g. via
request.raw_post
in Rails). Refer to the example code on the right. - Compare your generated signature against the signature provided with our webhook POST request. The two signatures should be identical. It is recommended to use a "constant time" string comparison (such as
secure_compare
in Rails) to protect against timing attacks. - If the signature is valid, check if the difference between the current timestamp and the
timestamp
parameter (when we send the webhook) is within your tolerance.
Webhook Request Format
{
"event_name": "conversation.ended",
"event_time": "2017-05-19T12:11:03.321+08:00",
"event_payload": { ... }
}
Each webhook request includes a JSON object formatted as follows:
Attribute | Type | Description |
---|---|---|
event_name | string | Name of event that triggers the webhook request to your provided URL. |
event_time | string | Timestamp of when webhook is triggered. |
event_payload | hash | Data relating to the event. |
Acknowledging a Webhook
You should acknowledge receipt of a webhook by returning a HTTP 200 status code. All other HTTP codes will be interpreted as a failed webhook delivery. We will attempt to re-deliver the webhook up to 4 more times, with an exponential backoff.
Events
conversation.ended
{
"event_name": "conversation.ended",
"event_time": "2017-05-19T12:11:03.321+08:00",
"event_payload": {
"conversation": {
"id": 12513522,
"started_at": "2018-08-16T06:32:00.000Z",
"handover_rep": {
"id": 95233,
"type": "sales_rep"
},
"leads": [
{
"id": 32244,
"email": "clara.smith@saleswhale.com",
"is_main": false,
"is_original": true
},
{
"id": 32552,
"email": "michael.wyke@saleswhale.com",
"is_main": true,
"is_original": false
}
],
"status": "qualified",
"enrolment": {
"id": 2214225,
"status": "processed",
"campaign_id": 2245,
"lead_id": 32244,
"errors": null,
"conversation_id": 12513522
}
}
}
}
This event is triggered when a Conversation reaches a Terminal State (indicated by the status
property under the conversation
object). Possible Terminal States are detailed in Show a Conversation. The event_payload
will include a Conversation object.
Trigger
conversation.ended
event will be triggered whenever a conversation status changes to a 'terminal' status.
A conversation can either be ongoing (non-terminal statuses) or completed (terminal statuses). Refer to Conversation statuses.
Interpreting the response
The response returns the following properties in the event_payload
object:
Property | Type | Description |
---|---|---|
conversation | object | You can retrieve details on the conversation that triggered the webhook. With the id property under the conversation object, you can retrieve the conversation details.With the id of the handover_rep under conversation object, you can do a matching to retrieve the sales rep it was handed over to. |
leads | array | For most conversations, there will only be one lead returned. In a conversation where your lead refers the AI assistant to their colleague, or CCs in other people, there will be multiple leads in a conversation. All leads within a conversation will have the same conversation status. With the id property under the lead object, you will be able to retrieve the details of each lead.is_main refers to the main lead when the conversation is completed, which is assumed to be the last person who replied to the AI assistant. If marked as true, this lead is the 'To' recipient. If false, this lead is either a 'CC' or 'BCC' recipient.is_original refers to the lead we first started the conversation on. |
status | string | This is the status of the conversation when the webhook was triggered. You can read more on conversation statuses here. |
enrollment | object | You can retrieve details on the enrolment that triggered the conversation. With the campaign_id property under the enrolment object, you can do a matching and retrieve the campaign the lead was enrolled to.With the enrolment.lead_id, you can retrieve the original lead whom the conversation was started on. |
email.sent
{
"event_name": "email.sent",
"event_time": "2019-11-08T08:06:22.472+00:00",
"event_payload": {
"email": {
"cc": [],
"to": [
"jeremy@example.com"
],
"from": "david@example.com",
"snippet": "Hi Jeremy, would your company be interested in a great product?",
"subject": "Are you interested, Jeremy?",
"created_at": "2019-11-08T08:03:37.000Z",
"conversation_id": 12513522
}
}
}
This event is triggered when the AI assistant sends an Email to a Lead.
Trigger
email.sent
event will be triggered whenever an email is sent by the AI.
Interpreting the response
The response returns the following properties in the event_payload
object:
Property | Type | Description |
---|---|---|
object | Contains the details of the email that triggered this webhook. |
The Email object returns the following properties:
Property | Type | Description |
---|---|---|
to | array | An array of strings, containing email addresses of recepients. |
cc | array | An array of strings, containing email addresses of carbon copy recepients. |
from | string | The email address of the sender. |
snippet | string | Short summary of the email body, truncated to maximum of 117 characters |
subject | string | Subject of this email. |
created_at | string | Time when this email was created. |
conversation_id | integer | With conversation_id , you can retrieve the conversation details relating to the email that triggered the webhook. |
lead.marked_do_not_contact
{
"event_name": "lead.marked_do_not_contact",
"event_time": "2020-03-11T12:11:03.321+08:00",
"event_payload": {
"lead": {
"id": 7094,
"email": "albertina+1a120320@example.net",
"first_name": "Albertina",
"last_name": "1a120320",
"job_title": null,
"follow_up_at": null,
"account_name": null,
"lead_owner": {
"id": 13,
"type": "sales_rep"
},
"do_not_contact": true,
"custom_attribute_values": [
{
"custom_attribute_id": 45,
"value": "13-08-2019",
"custom_attribute_name": "child_1_intended_commencement_date"
}
]
}
}
}
This event is triggered when a Lead is marked as Do Not Contact. The event_payload
will include a Lead object.
Trigger
lead.marked_do_not_contact
event will be triggered whenever a lead is marked as Do Not Contact, or do_not_contact
is set to true
.
Interpreting the response
The response returns the following properties in the event_payload
object:
Property | Type | Description |
---|---|---|
lead | object | You can retrieve details on the Lead that triggered the webhook. |
Changelog
2023-01-17
- Removed
rejection_reason
property from the Conversation object for Conversation endpoints and conversation.ended webhook.
2022-09-30
- Added clarification for a null conversation_id on a successful Enrolment
2022-08-18
- Added clarification on which Campaign
status
es are available for Leads to be enrolled to.
2022-08-13
- Added
updated_at
property for List all Campaigns endpoint. - Added
updated_at__is_after
query parameter to filter Campaigns by updated_at time.
2022-08-12
- Added pagination for List all Campaigns endpoint.
2022-08-04
- Added
status
property for List all Campaigns endpoint. - Added
status[]
query parameter to filter Campaigns by status.
2021-05-10
- Deprecated V1 API.
2020-09-30
- Human Review is no longer considered a terminal status, following the behaviour in-app.
2020-07-22
- Added API for fetching the authenticated team.
- Added section on pagination.
- Added API for fetching all conversations.
2020-07-21
- Updated Conversation object with new
referral_canceled
status.
2020-07-08
- Added
email
property for each lead in theleads
property of Conversation object for Conversation endpoints and conversation.ended webhook.
2020-06-30
- Added
rejection_reason
property to the Conversation object for Conversation endpoints and conversation.ended webhook.
2020-03-20
- Added
lead.marked_do_not_contact
webhook event.
2020-03-19
- Added
conversation_id
to the Email property in the email.sent webhook event.
2019-11-15
- Added
email.sent
webhook event.
2019-05-14
- Added API for fetch conversations of a lead
2019-05-07
- Added API for updating leads
2019-02-27
- Added
is_main
andis_original
to the Leads property in the Show a Conversation endpoint and conversation.ended webhook event.
2018-09-13
- Added a
conversation.ended
webhook event.
2018-08-29
- Initial release of V2 API, organised around resources for future extensibility. Currently supports the following use cases:
- Leads: Create, retrieve and delete Leads
- Campaigns, Custom Attributes, Sales Reps: List all
- Enrolments: Enrol a Lead into a Campaign
- Conversations: Retrieve (to check status) and cancel
2018-08-01
- Added an
allow_existing_lead
option to V1 Import Leads allow enrolment of existing leads. This will not update properties of the Lead.