NAV
JSON cURL Python PHP C#

Introduction

Welcome to the Number™ REST API documentation.
Use our APIs to seamlessly integrate Number™ functionalities into your website or application.

Client libraries

Number™ has official libraries that are available in several languages:

Supported channels

WhatsApp Business

You can send and receive WhatsApp messages for customer support, alerts, and notifications. Currently, you don’t need to specify the WhatsApp channel as a destination when making Sendbee API calls, as it’s the only channel we support. More channels will be coming soon.

WhatsApp Opt-In Requirements

WhatsApp requires that a contact must first consent to receive messages in WhatsApp by opting into them via a third-party channel. This can be any channel your business uses to communicate with people today — your website, app, email, SMS, retail location, etc. Sending users messages without an opt-in may result in users blocking your business and suspension of your WhatsApp business account.

To learn more, please visit WhatsApp Business API guide

WhatsApp consent

Notifications (Message Templates)

WhatsApp requires that any initiated messages sent by your business must be a pre-approved templated message. These messages are called Message Templates, with the exception of messages sent as a reply to a contact-initiated message (see the Customer Care Messages section below for more details). Currently only text messages can be sent as Message Templates.

To learn more, please visit our guide for sending WhatsApp notifications using message templates.

Customer Care Messages

To have a 2-way conversation with a contact, you need to receive messages from them. Contacts can send you a message in response to a Message Template or directly via WhatsApp click-to-chat link (see WhatsApp click-to-chat link section below).

WhatsApp 24-hour customer care window

A WhatsApp 24-hour customer care window begins when a contact-initiated message was sent to your business. Sessions are valid for 24 hours after the most recently received message, during which time you can communicate with contacts using free form messages. These messages are called Customer Care Messages. In order to send a message outside the 24-hour custom care window, you must use a pre-approved Message Template (see the Notifications section above for more details).

WhatsApp’s click to chat feature allows your customers to begin a chat with your business on WhatsApp without having your phone number saved in their phone’s address book. By clicking the link, a chat with your business automatically opens. Click to chat link works on WhatsApp mobile apps and WhatsApp Web.

To create your link use https://wa.me/ where the is a full phone number in international format. Omit any zeroes, brackets, or dashes when adding the phone number in international format.

Examples:
Use: https://wa.me/15551234567
Don’t use: https://wa.me/+001-(555)1234567–

To learn more, please visit WhatsApp click to chat link guide.

How to make a request

Every request to the API should be made with the following data in headers:

{
    "X-Auth-Token": "...",
    "X-Api-Key": "...",
    "Accept": "application/json",
    "Content-Type": "application/json"
}

In order to use Number™ APIs, you need to have an active Number™ account. Create your account at www.number.tm.

Base API URL is https://api-v2.sendbee.io

To authenticate a request to the API, you must create an Auth token as described in Authentication section and send it in authorization header using X-Auth-Token key. Also, API key should be added in header mapped with X-Api-Key key.

Request header name Request header value
X-Api-Key Your API key (available in the Number™ app).
X-Auth-Token Generated auth token. See Authentication for information on how to generate auth tokens.
Accept application/json
Content-Type application/json

Response

The response may contain data, meta, links, warning or error elements.

Response with pagination elements

Response with pagination elements:

{
    "data": {...},
    "meta": {
        "current_page": ...,
        "from": ...,
        "to": ...,
        "total": ...,
        "per_page": ...,
        "last_page": ...
    },
    "links": {
        "first": "...",
        "last": "...",
        "next": "...",
        "prev": "..."
    }
}

When making calls to the Number™ API, in some cases, API endpoints could return a list of data consisting of a larger number of elements. To handle this efficiently, we implemented pagination. Paginated responses are typically returned when making a GET request or any other request that returns a list of data in the response. The paginated response includes three main elements: data, meta, and links.

Response without pagination elements

Response without pagination elements:

{
    "data": {...}
}

Responses without pagination are usually returned when making POST, PUT, DELETE, or any other request that doesn’t return a list of data in the response. The unpaginated response contains only a ‘data’ element.

Warnings

Warning in response:

{
    "warning": "...",
    "data": {...},
    ...
}

If something goes wrong in an API request, an error or a warning will be returned. Warnings are usually returned for non-fatal conditions, indicating the issues that should be addressed but do not prevent the request from being completed successfully. On the other hand, errors are only returned for fatal conditions that prevent the request from being fulfilled.

Errors

Error in response:

{
    "error": {
        "detail": "...",
        "type": "..."
    }
}

In case of an error, the response will only contain the error data. Errors typically occur when a parameter is missing, invalid, or when attempting to update a contact using a contact ID that doesn’t exist. Errors typically occur when a parameter is missing, is invalid, or when attempting to update a contact using a contact ID that doesn’t exist. When such errors occur, the response will provide specific details about the error, helping you identify the issue and take appropriate actions to resolve it.

Authentication


import hmac, base64, hashlib
from datetime import datetime, timezone

API_SECRET = '...'

# step 1: get current timestamp
timestamp = str(int(
    datetime.now(timezone.utc).timestamp()
)).encode('utf-8')

# step 2: encrypt timestamp with your API secret to get a hash
encrypted = hmac.new(
    API_SECRET, data, hashlib.sha256
).hexdigest()

# step 3: concatenate timestamp and hash
timestamp_encrypt = '{}.{}'.format(
    timestamp.decode("utf-8"), encrypted.decode("utf-8")
).encode('utf-8')

# step 5: translate concatenated string into base64
auth_token = base64.b64encode(ts_encrypt).decode("utf-8")
<?php
// your API secret
$API_SECRET = '...';


// preferred method: using PHP API
$requestIsValid = \Sendbee\Api\Client::generateToken($API_SECRET);



// manual method (if you don't want to use the PHP API library)

// step 1: get current timestamp
$timestamp = time();

// step 2: encrypt timestamp with your API secret
$hashedData = hash_hmac('sha256', $timestamp, $API_SECRET, false);

// step 3: concatenate timestamp and encrypted_b64
$timestamp_encrypt = $timestamp . '.' . $hashedData;

// step 4: translate concatenated string into base64
$auth_token = base64_encode($timestamp_encrypt);

?>
using System;
using System.Security.Cryptography;
using System.Text;

public class Program
{
    public static void Main()
    {
        Console.WriteLine(getAuthKey());
    }

    public static string getAuthKey()
    {
        string apiSecret = "...";

        Int32 timestamp = (Int32)(DateTime.UtcNow.Subtract(new DateTime(1970, 1, 1))).TotalSeconds;
        string hashedData = HashHmac(timestamp.ToString(), apiSecret);
        return Base64Encode(timestamp.ToString() + "." + hashedData);
    }

    private static string HashHmac(string data, string key)
    {
        using (HMACSHA256 hmac = new HMACSHA256(Encoding.ASCII.GetBytes(key)))
        {
            byte[] a2 = hmac.ComputeHash(Encoding.ASCII.GetBytes(data));
            return String.Concat(Array.ConvertAll(a2, x => x.ToString("x2")));
        }
    }

    public static string Base64Encode(string plainText)
    {
        var plainTextBytes = System.Text.Encoding.UTF8.GetBytes(plainText);
        return System.Convert.ToBase64String(plainTextBytes);
    }

}

Every request to the API should be made with the following data in headers:

{
    "X-Auth-Token": "...",
    "X-Api-Key": "...",
    "Accept": "application/json",
    "Content-Type": "application/json"
}

The Number™ API utilizes the Auth token to authenticate requests. To create an Auth token for each request, you will need the API Secret.

Follow these steps to create an authentication token:

1. get current timestamp (in seconds)
2. encrypt the timestamp with your API secret key using HMAC sha256 encrypt algorithm (the output should be lowercase hexadecimal)
3. concatenate timestamp and encrypted timestamp separated by a . (dot)
4. translate the concatenated string into base64

Please check out the official Number™ API client libraries.
If you are using any of these languages, we highly recommend using our client library. It handles authentication for you, so you don’t need to worry about the authentication process and simplifies the integration, allowing you to focus on building your application.

If you’re using a programming language other than the ones for which we provide client libraries, please consider using these examples as a guide on how to create a valid auth token:

Step-by-step tutorial

If you’re encountering difficulties in generating an authentication token successfully, we recommend following this step-by-step tutorial to test your code. For this tutorial, we will be using predefined values for the Secret key and Current timestamp, making the process easier to understand and implement.

Step 1: get current timestamp (in seconds)
The step result is:
1586286575

Step 2: encrypt the timestamp with your API secret key using HMAC sha256 encrypt algorithm
The step result is (lowercase hexadecimal):
675ff7bd7674f940aec62dadad81fbf79d1f92876d79145ad25af2bf64faee41

Step 3: concatenate timestamp and encrypted timestamp separated by a . (dot)
The step result is:
1586286575.675ff7bd7674f940aec62dadad81fbf79d1f92876d79145ad25af2bf64faee41

Step 4: translate the concatenated string into base64
The step result is:
MTU4NjI4NjU3NS42NzVmZjdiZDc2NzRmOTQwYWVjNjJkYWRhZDgxZmJmNzlkMWY5Mjg3NmQ3OTE0NWFkMjVhZjJiZjY0ZmFlZTQx

Authentication resources

If you are not familiar with some of the terminology or technology mentioned in the steps above, we recommend reading these resources to get a better understanding.

Timestamp:

HMAC:

base64:

Rate Limit

A rate limit refers to the maximum number of API calls that a specific business number can make within a specific time period. If this limit is exceeded, API requests will be throttled, resulting in a 429 error response.

It is important to note that regardless of how many API keys you create in your Number™ account, the rate limit will always be associated with and counted within a specific business number.

Error response

Response body:

{
    "detail": "Request was throttled. Expected availability in 5 seconds.",
    "error": true,
    "type": "throttled"
}

If an API call is throttled due to exceeding the rate limit, server will return the following error response:

429 Too Many Requests
https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/429

Response headers:

Key Value
Retry-After 5

How to handle throttled request

When using the Number™ API, it’s essential to be prepared to handle possible 429 error responses. If you encounter a 429 error, you should check for the Retry-After key in the response headers. This key provides information on how long you should wait before making your next API call.

The Number™ API allows a maximum of 60 calls per minute. For instance, if you make 60 API calls within 30 seconds, the next API call will be throttled, and you will receive a 429 error response with the Retry-After key in the response headers. The value of the Retry-After key will be 30, indicating that you should wait for 30 seconds before making your next API call. This helps ensure that you stay within the rate limit and avoid further throttling.

Error test endpoint

curl -X GET -G "https://api-v2.sendbee.io/rate-limit/error-test" \
    -H "X-Auth-Token: ..." \
    -H "X-Api-Key: ..." \
    -H "Accept: application/json" \
    -H "Content-Type": "application/json"



Error test response:

{
    "detail": "Request was throttled. Expected available in 5 seconds.",
    "error": true,
    "type": "throttled"
}

The purpose of this endpoint is to return a 429 error response, and it doesn’t perform any actual functionality. However, it allows you to access all the necessary data for implementing your solution to handle a 429 error response effectively.

The best part about this endpoint is that any requests made to it won’t be counted towards your rate limit. You can freely query it as much as you want without the concern of hitting the rate limit error.

HTTP Request

GET https://api-v2.sendbee.io/rate-limit/error-test

Request test endpoint

curl -X GET -G "https://api-v2.sendbee.io/rate-limit/request-test" \
    -H "X-Auth-Token: ..." \
    -H "X-Api-Key: ..." \
    -H "Accept: application/json" \
    -H "Content-Type": "application/json"



Request test response:

{
    "detail": "Request was throttled. Expected availability in 5 seconds.",
    "error": true,
    "type": "throttled"
}

This endpoint doesn’t do anything, it will return 429 error response once you query it in rate higher than 60 call per minute. With this endpoint you can test your 429 error response handle implementation in real life environment.

Requests to this endpoint add to your rate limit counting, meaning you need to be careful and not to use this endpoint once your app is in the production.

The purpose of this endpoint is to return a 429 error response if you query it at a rate higher than 60 calls per minute. With this endpoint, you have the opportunity to test your 429 error response handling implementation in a production environment.

Requests made to this endpoint count towards your rate limit, so it’s essential to be cautious and avoid using this endpoint in the production environment.

HTTP Request

GET https://api-v2.sendbee.io/rate-limit/request-test

Teams

A team on Sendbee platform is a group of people working for your company using the platform to communicate with customers. Each team consists of members. You can have more then one team and each team can have a number of team members.

Fetch teams

curl -X GET -G "https://api-v2.sendbee.io/teams/teams" \
    -H "X-Auth-Token: ..." \
    -H "X-Api-Key: ..." \
    -H "Accept: application/json" \
    -H "Content-Type": "application/json"
from sendbee_api import SendbeeApi

api = SendbeeApi(
    '__your_api_key_here__', '__your_secret_key_here__'
)

teams = api.teams([member_id='...'])

for team in teams:
    team.id
    team.name

    for member in team.members:
        member.id
        member.name
        member.role
        member.online
        member.available


Teams fetch response:

{
   "data":[
      {
         "id": "...",
         "name": "...",
         "members": [
            {
               "id": "...",
               "name": "...",
               "online": true|false,
               "available": true|false,
               "role": "owner|admin|team_leader|member"
            },
            ...
         ]
      },
      ...
   ],
   "meta": {
      "current_page": ...,
      "from": ...,
      "to": ...,
      "total": ...,
      "per_page": ...,
      "last_page": ...
   },
   "links": {
      "first": "...",
      "last": "...",
      "next": "...",
      "prev": "...",
   }
}

Fetch teams within your account on Sendbee platform.
With this endpoint you can fetch a list of your teams, and every team in the list contains a list of members.
If you send member_id parameter, you will get all teams where that person is a member.

HTTP Request

GET https://api-v2.sendbee.io/teams/teams

URI Parameters

Parameter Default Required Description
member_id Provide a member ID in order to get only teams where that person is a member

Fetch team members

curl -X PUT -G "https://api-v2.sendbee.io/teams/members" \
    -H "X-Auth-Token: ..." \
    -H "X-Api-Key: ..." \
    -H "Accept: application/json" \
    -H "Content-Type": "application/json"
from sendbee_api import SendbeeApi

api = SendbeeApi(
    '__your_api_key_here__', '__your_secret_key_here__'
)

members = api.team_members([team_id='...'])

for member in members:
    member.id
    member.name
    member.role
    member.online
    member.available

    for team in member.teams:
        team.id
        team.name


Team members fetch response:

{
   "data":[
      {
         "id": "...",
         "name": "...",
         "online": true|false,
         "available": true|false,
         "role": "owner|admin|team_leader|member",
         "teams":[
            {
               "id": "...",
               "name": "..."
            },
            ...
         ]
      },
      ...
   ],
   "meta": {
      "current_page": ...,
      "from": ...,
      "to": ...,
      "total": ...,
      "per_page": ...,
      "last_page": ...
   },
   "links": {
      "first": "...",
      "last": "...",
      "next": "...",
      "prev": "...",
   }
}

Fetch team members within your account on Sendbee platform.
With this endpoint you can fetch a list of users from your company (team members) using Sendbee platform, and every member in the list contains a list of teams where he or she is a member.
If you send team_id parameter, you will get a list of members for that team.

HTTP Request

GET https://api-v2.sendbee.io/teams/members

URI Parameters

Parameter Default Required Description
team_id Provide a team ID in order to get team members only for that team

Contacts

A contact is a person/entity with whom you have communicated or that subscribed to receive messages from your business. The Sendbee API gives you the ability to subscribe, fetch or update your contacts.

Subscribe contact

curl -X POST -G "https://api-v2.sendbee.io/contacts/subscribe" \
    -H "X-Auth-Token: ..." \
    -H "X-Api-Key: ..." \
    -H "Accept: application/json" \
    -H "Content-Type": "application/json" \
    -d '{"phone": "...", "name": "..."}'
from sendbee_api import SendbeeApi

api = SendbeeApi(
    '__your_api_key_here__', '__your_secret_key_here__'
)

contact = api.subscribe_contact(
    phone='+...',
    # this is mandatory the most important information
    # about the subscribing contact

    [tags=['...', ...]], 
    # tag new contact
    # if tag doesn't exist, it will be created

    [name='...'], 

    [notes=[...]], 
    # write notes about your new subscriber

    [contact_fields={'__field_name__': '__field_value__', ...}],
    # fill contact fields with your data (value part)
    # contact fields must be pre-created in Sendbee Dashboard
    # any non-existent field will be ignored 

    [block_notifications=[True|False]],
    # prevent sending browser push notification and email 
    # notification to agents, when new contact subscribes
    # (default is True) 

    [block_automation=[True|False]]
    # prevent sending automated template messages to newly
    # subscribed contact (if any is set in Sendbee Dashboard) 
    # (default is True) 
)

contact.id
contact.status
contact.folder
contact.created_at

contact.name
contact.phone

for tag in contact.tags:
    tag.id
    tag.name

for note in contact.notes:
    note.value

for contact_field in contact.contact_fields:
    contact_field.key
    contact_field.value
<?php
$sendbeeApi = new \Sendbee\Api\Client($public_key, $secret);

$contactData = [
    // contact phone number, MANDATORY
    'phone' => '+...',

    // feel free to specify other optional contact data here
    // ...
];

try {
    $response = $sendbeeApi->subscribeContact($contactData);
} catch (\Sendbee\Api\Support\DataException $ex) {
    // handle missing data
    // this happens when required data was not provided
    echo "Could not subscribe a contact. ", $ex->getMessage();
} catch (\Exception $ex) {
    // handle exception thrown by GuzzleHttp
    // this is most likely due to a network issue
    echo "Could not contact backend endpoint. ", $ex->getMessage();
}

if ($response->isSuccess()) {
    /**
     * @var $contact \Sendbee\Api\Models\Contact
     */
    $contact = $response->getData();

    // contact is now subscribed (created)
    // $contact contains the newly created contact data
} else {
    /**
     * @var $error \Sendbee\Api\Transport\ResponseError
     */
    $error = $response->getError();
    if ($error) {
        // handle error
        echo "\n error type: ", $error->type;
        echo "\n error details: ", $error->detail;
    }
}

?>

Subscribe contact response:

{
    "data": {
        "id": "...",
        "name": "...",
        "phone": "...",
        "created_at": "...",
        "tags": ["...", ...],
        "status": "...",
        "folder": "...",
        "contact_fields": [
            {
                "key": "...",
                "value": "..."
            },
            ...
        ],
        "notes": ["...", ...]
    }
}

By subscribing a contact, you create a new contact as well.

But to be in compliance with WhatsApp Opt-In rules, you should subscribe/create only contacts who consent to receive your messages in WhatsApp.

Contact consent could be received in form of the web form on your website, having some kind of “Receive your purchase information on WhatsApp” checkbox.
It means contacts are subscribing to your business WhatsApp communication channel.

Read more about WhatsApp Opt-In rules.

HTTP Request

POST https://api-v2.sendbee.io/contacts/subscribe

Data Parameters

Parameter Default Required Description
phone yes Contact phone number. Number must be passed in the E.164 format: +/country code/area code/phone number, UK number example: +442071838750.
tags Tags for this contact (if passed tag name doesn’t match any existing tag name, it will be created as a new one).
name Contact name.
notes Custom notes about the contact.
contact_fields Contact fields defined in your contact fields list. A contact field must be pre-created via the Sendbee Dashboard or Sendbee API. Any non-existent field will be ignored. Send it in the following format: {“[field_name]”: “[field_value]”, …}.
block_notifications True Prevent sending browser push notification and email notification to agents when new contact subscribes.
block_automation True Prevent sending automated template messages to newly subscribed contact (applicable only if an automation rule was created in the Sendbee Dashboard).

Fetch contacts

curl -X GET -G "https://api-v2.sendbee.io/contacts" \
    -H "X-Auth-Token: ..." \
    -H "X-Api-Key: ..." \
    -H "Accept: application/json" \
    -H "Content-Type": "application/json"
from sendbee_api import SendbeeApi

api = SendbeeApi(
    '__your_api_key_here__', '__your_secret_key_here__'
)

contacts = api.contacts(
    [tags=['...', ...]], [status='subscribed|unsubscribed'], 
    [search_query='...'], [page=...], [limit=...]
)

for contact in contacts:
    contact.id
    contact.status
    contact.folder
    contact.created_at

    contact.name
    contact.phone

    for tag in contact.tags:
        tag.id
        tag.name

    for note in contact.notes:
        note.value

    for contact_field in contact.contact_fields:
        contact_field.key
        contact_field.value
<?php
$sendbeeApi = new \Sendbee\Api\Client($public_key, $secret);

// optional parameters
$params = [
    'tags' => '', // Filter contacts by tag
    'status' => '', // Filter contacts by status
    'search_query' => '', // Filter contacts by query string
    'page' => 1 // Page number for pagination
];

try {
    $response = $sendbeeApi->getContacts($params);
} catch (\Exception $ex) {
    // handle exception thrown by GuzzleHttp
    // this is most likely due to a network issue
    echo "Could not contact backend endpoint. ", $ex->getMessage();
}

if ($response->isSuccess()) {
    // everything is OK
    $data = $response->getData();
} else {
    /**
     * @var $error \Sendbee\Api\Transport\ResponseError
     */
    $error = $response->getError();
    if ($error) {
        echo "\n error type: ", $error->type;
        echo "\n error details: ", $error->detail;
    }
}
?>

Fetch contacts response:

{
    "data": [
        {
            "id": "...",
            "name": "...",
            "phone": "...",
            "created_at": "...",
            "tags": ["...", ...],
            "status": "subscribed|unsubscribed",
            "folder": "open|done|spam",
            "contact_fields": [
              {
                  "key": "...",
                  "value": "..."
              },
              ...
            ],
            "notes": ["...", ...]
        },
        ...
    ],
    "meta": {
        "current_page": ...,
        "from": ...,
        "to": ...,
        "total": ...,
        "per_page": ...,
        "last_page": ...
    },
    "links": {
        "first": "...",
        "last": "...",
        "next": "...",
        "prev": "..."
    }
}

HTTP Request

GET https://api-v2.sendbee.io/contacts

URI Parameters

Parameter Default Required Description
phone Contact phone number. Number must be passed in the E.164 format: +/country code/area code/phone number, UK number example: +442071838750.
tags Filter contacts by tags.
status Filter contacts by subscription status. Use subscribed or unsubscribed value.
search_query Filter contacts by string. The query will search through name and phone number fields.
page 1 Page number for pagination.
limit 10 Number of items per page. Maximum is 100.

Update contact

curl -X PUT -G "https://api-v2.sendbee.io/contacts" \
    -H "X-Auth-Token: ..." \
    -H "X-Api-Key: ..." \
    -H "Accept: application/json" \
    -H "Content-Type": "application/json" \
    -d '{"id": "...", ...}'
from sendbee_api import SendbeeApi

api = SendbeeApi(
    '__your_api_key_here__', '__your_secret_key_here__'
)

contact = api.update_contact(
    id='...',
    # contact is identified with ID

    [phone='+...'],
    # this is the most important information 
    # about the subscribing contact

    [tags=['...', ...]], 
    # tag a contact
    # if tag doesn't exist, it will be created
    # if you want to remove a tag from a contact, put "-" before the tag name
    # example: tags=['-tag1'] # this will remove a "tag1" tag from a contact

    [name='...'],

    [notes=[...]], 
    # write notes about your new subscriber
    # if there are notes already saved for this contact
    # new notes will be appended

    [contact_fields={'__field_name__': '__field_value__', ...}],
    # fill contact fields with your data (value part)
    # contact fields must be pre-created in Sendbee Dashboard
    # any non-existent field will be ignored 
    # if there are fields already filled with data for this contact
    # it will be overwritten with new data 
)

contact.id
contact.status
contact.folder
contact.created_at

contact.name
contact.phone

for tag in contact.tags:
    tag.id
    tag.name

for note in contact.notes:
    note.value

for contact_field in contact.contact_fields:
    contact_field.key
    contact_field.value
<?php
$contactData = [
    // contact id, MANDATORY
    'id' => '...',

    // feel free to specify other optional contact data here
    // ...
];

try {
    $response = $sendbeeApi->updateContact($contactData);
} catch (\Sendbee\Api\Support\DataException $ex) {
    // handle missing data
    // this happens when required data was not provided
    echo "Could not update a contact. ", $ex->getMessage();
} catch (\Exception $ex) {
    // handle exception thrown by GuzzleHttp
    // this is most likely due to a network issue
    echo "Could not contact backend endpoint. ", $ex->getMessage();
}

if ($response->isSuccess()) {
    /**
     * @var $contact \Sendbee\Api\Models\Contact
     */
    $contact = $response->getData();

    // contact is now updated
    // $contact contains the updated contact data

} else {
    /**
     * @var $error \Sendbee\Api\Transport\ResponseError
     */
    $error = $response->getError();
    if ($error) {
        // handle error
        echo "\n error type: ", $error->type;
        echo "\n error details: ", $error->detail;
    }
}
?>

Update contacts response:

{
    "data": {
        "id": "...",
        "name": "...",
        "phone": "...",
        "created_at": "...",
        "tags": ["...", ...],
        "status": "subscribed|unsubscribed",
        "folder": "open|done|spam",
        "contact_fields": [
            {
                "key": "...",
                "value": "..."
            },
            ...
        ],
        "notes": ["...", ...]
    }
}

HTTP Request

PUT https://api-v2.sendbee.io/contacts

Data Parameters

Parameter Default Required Description
id Yes Contact ID
tags Tag a contact, if tag doesn’t exist, it will be created. If you want to remove a tag from the contact, put “-” (without quotations) before the tag name. (add tag example: tags=[“new_customer”] - this will add a “new_customer” tag to the contact) (remove tag example: tags=[“-new_customer”] - this will remove a “new_customer” tag from the contact)
name Contact name
notes Custom notes about the contact. Take care, notes are not replaced but are instead appended to existing notes when updating a contact.
contact_fields Contact fields defined in your contact fields list. A contact field must be pre-created in the Sendbee Dashboard or via Sendbee API. Any non-existent field will be ignored. Send it in the following format: {“[field_name]”: “[field_value]”, …}.

Contact tags

A contact tag is a label that can be used to classify contacts. The Sendbee API gives you the ability to fetch, create, update or delete your contact tags.

Fetch contact tags

curl -X GET -G "https://api-v2.sendbee.io/contacts/tags" \
    -H "X-Auth-Token: ..." \
    -H "X-Api-Key: ..." \
    -H "Accept: application/json" \
    -H "Content-Type": "application/json"
from sendbee_api import SendbeeApi

api = SendbeeApi(
    '__your_api_key_here__', '__your_secret_key_here__'
)

tags = api.tags([name='...'], [page=...], [limit=...])

for tag in tags:
    tag.id
    tag.name
<?php
$sendbeeApi = new \Sendbee\Api\Client($public_key, $secret);

// optional parameters
$params = [
    'name' => '...', // Name of the tag
    'page' => 1 // Page number for pagination
];

try {
    $response = $sendbeeApi->getTags($params);
} catch (\Exception $ex) {
    // handle exception thrown by GuzzleHttp
    // this is most likely due to a network issue
    echo "Could not contact backend endpoint. ", $ex->getMessage();
}

if ($response->isSuccess()) {
    // everything is OK

    $data = $response->getData();

    foreach ($data as $tag) {
        /**
         * @var $tag \Sendbee\Api\Models\ContactTag
         */
        echo "\n ID: ", $tag->id;
        echo "\n name: ", $tag->name;
    }
} else {
    /**
     * @var $error \Sendbee\Api\Transport\ResponseError
     */
    $error = $response->getError();
    if ($error) {
        echo "\n error type: ", $error->type;
        echo "\n error details: ", $error->detail;
    }
}
?>

Fetch tags response:

{
   "data": [
       {
           "id": "...",
           "name": "...",
       },
       ...
   ],
   "meta": {
       "current_page": ...,
       "from": ...,
       "to": ...,
       "total": ...,
       "per_page": ...,
       "last_page": ...
   },
   "links": {
       "first": "...",
       "last": "...",
       "next": "...",
       "prev": "..."
   }
}

HTTP Request

GET https://api-v2.sendbee.io/contacts/tags

URI Parameters

Parameter Default Required Description
name Tag name
page 1 Page number for pagination.
limit 10 Number of items per page. Maximum is 100.

Create contact tag

curl -X POST -G "https://api-v2.sendbee.io/contacts/tags" \
    -H "X-Auth-Token: ..." \
    -H "X-Api-Key: ..." \
    -H "Accept: application/json" \
    -H "Content-Type": "application/json" \
    -d '{"name": "..."}'
from sendbee_api import SendbeeApi

api = SendbeeApi(
    '__your_api_key_here__', '__your_secret_key_here__'
)

tag = api.create_tag(name='...')

tag.id
tag.name
<?php
$sendbeeApi = new \Sendbee\Api\Client($public_key, $secret);

$data = [
    // tag name, MANDATORY
    'name' => '...'
];

try {
    $response = $sendbeeApi->createTag($data);
} catch (\Sendbee\Api\Support\DataException $ex) {
    // handle missing data
    // this happens when required data was not provided
    echo "Missing required data. ", $ex->getMessage();
} catch (\Exception $ex) {
    // handle exception thrown by GuzzleHttp
    // this is most likely due to a network issue
    echo "Could not contact backend endpoint. ", $ex->getMessage();
}

if ($response->isSuccess()) {
    /**
     * @var $tag \Sendbee\Api\Models\ContactTag
     */
    $tag = $response->getData();

    // tag is now created
    // $tag contains the newly created tag data
} else {
    /**
     * @var $error \Sendbee\Api\Transport\ResponseError
     */
    $error = $response->getError();
    if ($error) {
        // handle error
        echo "\n error type: ", $error->type;
        echo "\n error details: ", $error->detail;
    }
}

?>

Create contact tag response:

{
    "data": {
        "id": "...",
        "name": "..."
    }
}

HTTP Request

POST https://api-v2.sendbee.io/contacts/tags

Data Parameters

Parameter Default Required Description
name yes Tag name

Update contact tag

curl -X PUT -G "https://api-v2.sendbee.io/contacts/tags" \
    -H "X-Auth-Token: ..." \
    -H "X-Api-Key: ..." \
    -H "Accept: application/json" \
    -H "Content-Type": "application/json" \
    -d '{"id": "...", "name": "..."}'
from sendbee_api import SendbeeApi

api = SendbeeApi(
    '__your_api_key_here__', '__your_secret_key_here__'
)

tag = api.update_tag(id='...', name='...')

tag.id
tag.name
<?php
$sendbeeApi = new \Sendbee\Api\Client($public_key, $secret);

$data = [
    // tag id, MANDATORY
    'id' => '...',
    // tag name, MANDATORY
    'name' => '...'
];

try {
    $response = $sendbeeApi->updateTag($data);
} catch (\Sendbee\Api\Support\DataException $ex) {
    // handle missing data
    // this happens when required data was not provided
    echo "Missing required data. ", $ex->getMessage();
} catch (\Exception $ex) {
    // handle exception thrown by GuzzleHttp
    // this is most likely due to a network issue
    echo "Could not contact backend endpoint. ", $ex->getMessage();
}

if ($response->isSuccess()) {
    /**
     * @var $tag \Sendbee\Api\Models\ContactTag
     */
    $tag = $response->getData();

    // tag is now updated
    // $tag contains the updated tag data
} else {
    /**
     * @var $error \Sendbee\Api\Transport\ResponseError
     */
    $error = $response->getError();
    if ($error) {
        // handle error
        echo "\n error type: ", $error->type;
        echo "\n error details: ", $error->detail;
    }
}
?>

Update contact tag response:

{
    "data": {
        "id": "...",
        "name": "..."
    }
}

HTTP Request

PUT https://api-v2.sendbee.io/contacts/tags

Data Parameters

Parameter Default Required Description
id yes Tag ID
name yes Tag name

Delete contact tag

curl -X DELETE -G "https://api-v2.sendbee.io/contacts/tags?id=..." \
    -H "X-Auth-Token: ..." \
    -H "X-Api-Key: ..." \
    -H "Accept: application/json" \
    -H "Content-Type": "application/json"
from sendbee_api import SendbeeApi

api = SendbeeApi(
    '__your_api_key_here__', '__your_secret_key_here__'
)

response = api.delete_tag(id='...')

response.message
<?php
$sendbeeApi = new \Sendbee\Api\Client($public_key, $secret);

$data = [
    // tag id, MANDATORY
    'id' => '...'
];

try {
    $response = $sendbeeApi->deleteTag($data);
} catch (\Sendbee\Api\Support\DataException $ex) {
    // handle missing data
    // this happens when required data was not provided
    echo "Missing required data. ", $ex->getMessage();
} catch (\Exception $ex) {
    // handle exception thrown by GuzzleHttp
    // this is most likely due to a network issue
    echo "Could not contact backend endpoint. ", $ex->getMessage();
}

if ($response->isSuccess()) {
    /**
     * @var $message \Sendbee\Api\Models\ServerMessage
     */
    $message = $response->getData();
    // record is now deleted
    // $message contains server info message
    print_r($message);

} else {
    /**
     * @var $error \Sendbee\Api\Transport\ResponseError
     */
    $error = $response->getError();
    if ($error) {
        // handle error
        echo "\n error type: ", $error->type;
        echo "\n error details: ", $error->detail;
    }
}
?>

Delete contact tag response:

{
    "data": {
        "message": "Tag deleted"
    }
}

HTTP Request

DELETE https://api-v2.sendbee.io/contacts/tags

URI Parameters

Parameter Default Required Description
id yes Tag ID

Contact fields

A contact field is a property associated with a contact, that can be populated with specific information such as job title, company details, website, and so on. The Sendbee API gives you the ability to fetch, create, update or delete your contact fields.

Each contact field has a specified data type. Ensure the data you submit when updating a contact field’s value is formatted correctly:

Type Example Value
String Zagreb, Croatia. String values
Boolean true Boolean true/false values
Number 50 Integers
Datetime 2020-01-15 03:04:05 YYYY-MM-DD HH:MM:SS
List [‘option1’, ‘option2’, …] List of options

Fetch contact fields

curl -X GET -G "https://api-v2.sendbee.io/contacts/fields" \
    -H "X-Auth-Token: ..." \
    -H "X-Api-Key: ..." \
    -H "Accept: application/json" \
    -H "Content-Type": "application/json"
from sendbee_api import SendbeeApi

api = SendbeeApi(
    '__your_api_key_here__', '__your_secret_key_here__'
)

contact_fields = api.contact_fields([search_query='...'], [page=...], [limit=...])

for contact_field in contact_fields:
    contact_field.name
    contact_field.type

    if contact_field.type == 'list':
        contact_field.options
<?php
$sendbeeApi = new \Sendbee\Api\Client($public_key, $secret);

$params = [
    'search_query' => '', // Filter by query string
    'page' => 1 // Page number for pagination
];

try {
    $response = $sendbeeApi->getContactFields($params);
} catch (\Exception $ex) {
    // handle exception thrown by GuzzleHttp
    // this is most likely due to a network issue
    echo "Could not contact backend endpoint. ", $ex->getMessage();
}

if ($response->isSuccess()) {
    // everything is OK

    $data = $response->getData();

    foreach ($data as $field) {
        /**
         * @var $tag \Sendbee\Api\Models\ContactField
         */
        echo "\n ID: ", $field->id;
        echo "\n type: ", $field->type;
        echo "\n name: ", $field->name;

        foreach ($field->options as $option) {
            /**
             * @var $option string
             */
            echo "\n field -> option: ", $option;
        }

    }
} else {
    /**
     * @var $error \Sendbee\Api\Transport\ResponseError
     */
    $error = $response->getError();
    if ($error) {
        echo "\n error type: ", $error->type;
        echo "\n error details: ", $error->detail;
    }
}
?>

Fetch contact fields response:

{
   "data": [
       {
           "id": "...",
           "type": "...",
           "name": "...",
           "options": ["...", ...]
       },
       ...
   ],
   "meta": {
       "current_page": ...,
       "from": ...,
       "to": ...,
       "total": ...,
       "per_page": ...,
       "last_page": ...
   },
   "links": {
       "first": "...",
       "last": "...",
       "next": "...",
       "prev": "..."
   }
}

HTTP Request

GET https://api-v2.sendbee.io/contacts/fields

URI Parameters

Parameter Default Required Description
search_query Filter contact fields by string. The query will search for name.
page 1 Page number for pagination.
limit 10 Number of items per page. Maximum is 100.

Create contact field

curl -X POST -G "https://api-v2.sendbee.io/contacts/fields" \
    -H "X-Auth-Token: ..." \
    -H "X-Api-Key: ..." \
    -H "Accept: application/json" \
    -H "Content-Type": "application/json" \
    -d '{"name": "...", "type": "text|number|list|date|boolean"}'
from sendbee_api import SendbeeApi

api = SendbeeApi(
    '__your_api_key_here__', '__your_secret_key_here__'
)

contact_field = api.create_contact_field(
    name='...', type='text|number|list|date|boolean'
)

contact_field.id
contact_field.name
contact_field.type

if contact_field.type == 'list':
    contact_field.options
<?php
$sendbeeApi = new \Sendbee\Api\Client($public_key, $secret);

$data = [
    // name, MANDATORY
    'name' => 'field name',
    // type, one of ['text', 'number', 'list', 'date', 'boolean'], MANDATORY
    'type' => 'text',
    // List of options. Send it only if the field type is a list.
    // values are strings
    'options' => []
];

try {
    $response = $sendbeeApi->createContactField($data);
} catch (\Sendbee\Api\Support\DataException $ex) {
    // handle missing data
    // this happens when required data was not provided
    echo "Missing required data. ", $ex->getMessage();
} catch (\Exception $ex) {
    // handle exception thrown by GuzzleHttp
    // this is most likely due to a network issue
    echo "Could not contact backend endpoint. ", $ex->getMessage();
}

if ($response->isSuccess()) {
    /**
     * @var $contactField \Sendbee\Api\Models\ContactField
     */
    $contactField = $response->getData();
    // contact field is now created
    // $contactField contains the newly created contact field data
    print_r($contactField);
} else {
    /**
     * @var $error \Sendbee\Api\Transport\ResponseError
     */
    $error = $response->getError();
    if ($error) {
        // handle error
        echo "\n error type: ", $error->type;
        echo "\n error details: ", $error->detail;
    }
}
?>

Create contact field response:

{
    "data": {
        "id": "...",
        "type": "...",
        "name": "...",
        "options": ["...", ...]
    }
}

When you subscribe or update a contact, use contact field name to link a field value to a contact.

If a contact field type is a list, then you need to send the options parameter.
Options parameter is a list of option names: ['option1', 'option2', ...]

HTTP Request

POST https://api-v2.sendbee.io/contacts/fields

Data Parameters

Parameter Default Required Description
name yes Field name.
type yes Field type. Can be string, number, list, date or boolean.
options List of options. Send it only if the field type is a list.

Update contact field

When you subscribe or update a contact, use contact field name to link a field value to a contact.

If a contact field type is a list, then you need to send the options parameter.
Options parameter is a list of option names: ['option1', 'option2', ...]

curl -X PUT -G "https://api-v2.sendbee.io/contacts/fields" \
    -H "X-Auth-Token: ..." \
    -H "X-Api-Key: ..." \
    -H "Accept: application/json" \
    -H "Content-Type": "application/json" \
    -d '{"id": "..."}'
from sendbee_api import SendbeeApi

api = SendbeeApi(
    '__your_api_key_here__', '__your_secret_key_here__'
)

contact_field = api.update_contact_field(
    id='...', [name='...'], [type='text|number|list|date|boolean']
)

contact_field.id
contact_field.name
contact_field.type

if contact_field.type == 'list':
    contact_field.options
<?php
$sendbeeApi = new \Sendbee\Api\Client($public_key, $secret);

$data = [
    // id, MANDATORY
    'id' => '...',
    // name, MANDATORY
    'name' => 'field name update',
    // type, one of ['text', 'number', 'list', 'date', 'boolean'], MANDATORY
    'type' => 'text',
    // List of options. Send it only if the field type is a list.
    // values are strings
    'options' => []
];

try {
    $response = $sendbeeApi->updateContactField($data);
} catch (\Sendbee\Api\Support\DataException $ex) {
    // handle missing data
    // this happens when required data was not provided
    echo "Missing required data. ", $ex->getMessage();
} catch (\Exception $ex) {
    // handle exception thrown by GuzzleHttp
    // this is most likely due to a network issue
    echo "Could not contact backend endpoint. ", $ex->getMessage();
}

if ($response->isSuccess()) {
    /**
     * @var $contactField \Sendbee\Api\Models\ContactField
     */
    $contactField = $response->getData();
    // contact field is now updated
    // $contactField contains the updated contact field data
    print_r($contactField);

} else {
    /**
     * @var $error \Sendbee\Api\Transport\ResponseError
     */
    $error = $response->getError();
    if ($error) {
        // handle error
        echo "\n error type: ", $error->type;
        echo "\n error details: ", $error->detail;
    }
}
?>

Update contact field response:

{
    "data": {
        "id": "...",
        "type": "...",
        "name": "..."
    }
}

HTTP Request

PUT https://api-v2.sendbee.io/contacts/fields

Data Parameters

Parameter Default Required Description
id yes Contact field id.
name Field name.
type Field type. Can be text, number, list, date, boolean.
options List of options. Send it only if the field type is a list.

Delete contact field

curl -X DELETE -G "https://api-v2.sendbee.io/contacts/fields?id=..." \
    -H "X-Auth-Token: ..." \
    -H "X-Api-Key: ..." \
    -H "Accept: application/json" \
    -H "Content-Type": "application/json"
from sendbee_api import SendbeeApi

api = SendbeeApi(
    '__your_api_key_here__', '__your_secret_key_here__'
)

response = api.delete_contact_field(id='...')

response.message
<?php
$sendbeeApi = new \Sendbee\Api\Client($public_key, $secret);

$data = [
    // id, MANDATORY
    'id' => '...',
];

try {
    $response = $sendbeeApi->deleteContactField($data);
} catch (\Sendbee\Api\Support\DataException $ex) {
    // handle missing data
    // this happens when required data was not provided
    echo "Missing required data. ", $ex->getMessage();
} catch (\Exception $ex) {
    // handle exception thrown by GuzzleHttp
    // this is most likely due to a network issue
    echo "Could not contact backend endpoint. ", $ex->getMessage();
}

if ($response->isSuccess()) {
    /**
     * @var $message \Sendbee\Api\Models\ServerMessage
     */
    $message = $response->getData();
    // record is now deleted
    // $message contains server info message
    print_r($message);

} else {
    /**
     * @var $error \Sendbee\Api\Transport\ResponseError
     */
    $error = $response->getError();
    if ($error) {
        // handle error
        echo "\n error type: ", $error->type;
        echo "\n error details: ", $error->detail;
    }
}
?>

Delete contact field response:

{
    "data": {
        "message": "Contact field deleted"
    }
}

HTTP Request

DELETE https://api-v2.sendbee.io/contacts/fields

URI Parameters

Parameter Default Required Description
id yes Contact field id

Conversations

A conversation is a unique thread of messages that can include Notifications (Message Templates) and Customer Care messages. The Sendbee API gives you the ability to fetch conversations, conversation messages, message templates and send message templates or customer care messages.

Fetch conversation list

curl -X GET -G "https://api-v2.sendbee.io/conversations" \
    -H "X-Auth-Token: ..." \
    -H "X-Api-Key: ..." \
    -H "Accept: application/json" \
    -H "Content-Type": "application/json"
from sendbee_api import SendbeeApi

api = SendbeeApi(
    '__your_api_key_here__', '__your_secret_key_here__'
)

conversations = api.conversations(
    [folder='open|done|spam'], [search_query='...'], 
    [date_from=__timestamp__], [date_to=__timestamp__], 
    [limit=...], [page=...]
)

for conversation in conversations:
    conversation.id
    conversation.folder
    conversation.chatbot_active
    conversation.platform
    conversation.created_at

    conversation.contact.id
    conversation.contact.name
    conversation.contact.phone

    conversation.last_message.direction
    conversation.last_message.status
    conversation.last_message.inbound_sent_at
    conversation.last_message.outbound_sent_at
<?php
// optional parameters
$params = [
    // Filter conversations by folder. Specify open, done or spam
    'folder' => '',
    // Any kind of string that will be used to perform filtering
    'search_query' => '',
    // Page number for pagination
    'page' => 1
];

try {
    $response = $sendbeeApi->getConversations($params);
} catch (\Exception $ex) {
    // handle exception thrown by GuzzleHttp
    // this is most likely due to a network issue
    echo "Could not contact backend endpoint. ", $ex->getMessage();
}


if ($response->isSuccess()) {
    // everything is OK
    $data = $response->getData();

    foreach ($data as $conversation) {
        /**
         * @var $conversation \Sendbee\Api\Models\Conversation
         */
        echo "\n ID: ", $conversation->id;
        echo "\n folder: ", $conversation->folder;
        echo "\n chatbot_active: ", $conversation->chatbot_active;
        echo "\n platform: ", $conversation->platform;
        echo "\n created_at: ", $conversation->created_at;

        echo "\n contact -> id: ", $conversation->contact->id;
        echo "\n contact -> name: ", $conversation->contact->name;
        echo "\n contact -> phone: ", $conversation->contact->phone;

        echo "\n last_message -> direction: ", $conversation->last_message->direction;
        echo "\n last_message -> status: ", $conversation->last_message->status;
        echo "\n last_message -> inbound_sent_at: ", $conversation->last_message->inbound_sent_at;
        echo "\n last_message -> outbound_sent_at: ", $conversation->last_message->outbound_sent_at;

    }
} else {
    /**
     * @var $error \Sendbee\Api\Transport\ResponseError
     */
    $error = $response->getError();
    if ($error) {
        echo "\n error type: ", $error->type;
        echo "\n error details: ", $error->detail;
    }
}
?>

Fetch conversations response:

{
    "data": [
        {
            "id": "...",
            "folder": "open|done|spam",
            "last_message": {
                "direction": "inbound|outbound",
                "status": "sent|received|delivered|read|failed",
                "inbound_sent_at": "...",
                "outbound_sent_at": "..."
            },
            "contact":{
                "id": "...",
                "phone": "...",
                "name": "..."
            },
            "chatbot_active": true|false,
            "platform": "whatsapp",
            "created_at": "2020-01-14 21:56:49"
        },
        ...
    ],
    "meta": {
        "current_page": ...,
        "from": ...,
        "to": ...,
        "total": ...,
        "per_page": ...,
        "last_page": ...
    },
    "links": {
        "first": "...",
        "last": "...",
        "next": "...",
        "prev": "..."
    }
}

HTTP Request

GET https://api-v2.sendbee.io/conversations

URI Parameters

Parameter Default Required Description
folder open, done or spam
search_query Filter conversations by string. The query will search through contact name and phone number fields.
date_from Filter conversations to the ones with the last message (inbound or outbound) AFTER this date and time. Should be UNIX timestamp.
date_to Filter conversations to the ones with the last message (inbound or outbound) BEFORE this date and time. Should be UNIX timestamp.
page 1 Page number for pagination.
limit 10 Number of items per page. Maximum is 100.

Fetch one conversation

curl -X GET -G "https://api-v2.sendbee.io/conversation" \
    -H "X-Auth-Token: ..." \
    -H "X-Api-Key: ..." \
    -H "Accept: application/json" \
    -H "Content-Type": "application/json"
from sendbee_api import SendbeeApi

api = SendbeeApi(
    '__your_api_key_here__', '__your_secret_key_here__'
)

conversation = api.get_conversation(conversation_id='__conversation__id__')

conversation.id
conversation.folder
conversation.chatbot_active
conversation.platform
conversation.created_at

conversation.contact.id
conversation.contact.name
conversation.contact.phone

conversation.last_message.direction
conversation.last_message.status
conversation.last_message.inbound_sent_at
conversation.last_message.outbound_sent_at
<?php
// parameters
$params = [
    // Conversation UUID, MANDATORY
    'conversation_id' => '...'
];
try {
    $response = $sendbeeApi->getConversation($params);
} catch (\Sendbee\Api\Support\DataException $ex) {
    // handle missing data
    // this happens when required data was not provided
    echo "Missing required data. ", $ex->getMessage();
} catch (\Exception $ex) {
    // handle exception thrown by GuzzleHttp
    // this is most likely due to a network issue
    echo "Could not contact backend endpoint. ", $ex->getMessage();
}
if ($response->isSuccess()) {
    // everything is OK
    /**
     * @var $conversation \Sendbee\Api\Models\Conversation
     */
    $conversation = $response->getData();
    echo "\n ID: ", $conversation->id;
    echo "\n folder: ", $conversation->folder;
    echo "\n chatbot_active: ", $conversation->chatbot_active;
    echo "\n platform: ", $conversation->platform;
    echo "\n created_at: ", $conversation->created_at;
    echo "\n contact -> id: ", $conversation->contact->id;
    echo "\n contact -> name: ", $conversation->contact->name;
    echo "\n contact -> phone: ", $conversation->contact->phone;
    echo "\n last_message -> direction: ", $conversation->last_message->direction;
    echo "\n last_message -> status: ", $conversation->last_message->status;
    echo "\n last_message -> inbound_sent_at: ", $conversation->last_message->inbound_sent_at;
    echo "\n last_message -> outbound_sent_at: ", $conversation->last_message->outbound_sent_at;
} else {
    /**
     * @var $error \Sendbee\Api\Transport\ResponseError
     */
    $error = $response->getError();
    if ($error) {
        echo "\n error type: ", $error->type;
        echo "\n error details: ", $error->detail;
    }
}
?>

Fetch conversation response:

[
    {
        "id": "...",
        "folder": "open|done|spam",
        "last_message": {
            "direction": "inbound|outbound",
            "status": "sent|received|delivered|read|failed",
            "inbound_sent_at": "...",
            "outbound_sent_at": "..."
        },
        "contact":{
            "id": "...",
            "phone": "...",
            "name": "..."
        },
        "chatbot_active": true|false,
        "platform": "whatsapp",
        "created_at": "2020-01-14 21:56:49"
    }
]

HTTP Request

GET https://api-v2.sendbee.io/conversation

URI Parameters

Parameter Default Required Description
conversation_id yes conversation UUID

Update conversation

curl -X PUT -G "https://api-v2.sendbee.io/conversation" \
    -H "X-Auth-Token: ..." \
    -H "X-Api-Key: ..." \
    -H "Accept: application/json" \
    -H "Content-Type": "application/json" \
    -d '{"conversation_id": ..., "folder": ...}'
from sendbee_api import SendbeeApi

api = SendbeeApi(
    '__your_api_key_here__', '__your_secret_key_here__'
)

conversations = api.update_conversation(
    conversation_id='__conversation__id__'
    folder='open|done|spam'
)

conversation.id
conversation.folder
conversation.chatbot_active
conversation.platform
conversation.created_at

conversation.contact.id
conversation.contact.name
conversation.contact.phone

conversation.last_message.direction
conversation.last_message.status
conversation.last_message.inbound_sent_at
conversation.last_message.outbound_sent_at
<?php
// parameters
$params = [
    // Conversation UUID, MANDATORY
    'conversation_id' => '...',
    // Assigned "folder" - 'open', 'done' or 'spam'
    'folder' => 'open|done|spam'
];
try {
    $response = $sendbeeApi->updateConversation($params);
} catch (\Sendbee\Api\Support\DataException $ex) {
    // handle missing data
    // this happens when required data was not provided
    echo "Missing required data. ", $ex->getMessage();
} catch (\Exception $ex) {
    // handle exception thrown by GuzzleHttp
    // this is most likely due to a network issue
    echo "Could not contact backend endpoint. ", $ex->getMessage();
}
if ($response->isSuccess()) {
    // everything is OK
    $data = $response->getData();
    foreach ($data as $message) {
        /**
         * @var $message \Sendbee\Api\Models\Message
         */
        echo "\n body: ", $message->body;
        echo "\n media_type: ", $message->media_type;
        echo "\n media_url: ", $message->media_url;
        echo "\n status: ", $message->status;
        echo "\n direction: ", $message->direction;
        echo "\n sent_at: ", $message->sent_at;
    }
} else {
    /**
     * @var $error \Sendbee\Api\Transport\ResponseError
     */
    $error = $response->getError();
    if ($error) {
        echo "\n error type: ", $error->type;
        echo "\n error details: ", $error->detail;
    }
}
?>

Update conversation response:

{
    "id": "...",
    "folder": "open|done|spam",
    "last_message": {
        "direction": "inbound|outbound",
        "status": "sent|received|delivered|read|failed",
        "inbound_sent_at": "...",
        "outbound_sent_at": "..."
    },
    "contact":{
        "id": "...",
        "phone": "...",
        "name": "..."
    },
    "chatbot_active": true|false,
    "platform": "whatsapp",
    "created_at": "2020-01-14 21:56:49"
}

HTTP Request

GET https://api-v2.sendbee.io/conversation

Data Parameters

Parameter Default Required Description
conversation_id yes conversation UUID
folder open, done or spam

Fetch conversation messages

curl -X GET -G "https://api-v2.sendbee.io/conversations/messages?conversation_id=..." \
    -H "X-Auth-Token: ..." \
    -H "X-Api-Key: ..." \
    -H "Accept: application/json" \
    -H "Content-Type": "application/json"
from sendbee_api import SendbeeApi

api = SendbeeApi(
    '__your_api_key_here__', '__your_secret_key_here__'
)

messages = api.messages(conversation_id='...')

for message in messages:
    message.body
    message.media_type
    message.media_url
    message.status
    message.direction
    message.sent_at
<?php
// parameters
$params = [
    // Conversation UUID, MANDATORY
    'conversation_id' => '...',
    // Page number for pagination
    'page' => 1
];

try {
    $response = $sendbeeApi->getMessages($params);
} catch (\Sendbee\Api\Support\DataException $ex) {
    // handle missing data
    // this happens when required data was not provided
    echo "Missing required data. ", $ex->getMessage();
} catch (\Exception $ex) {
    // handle exception thrown by GuzzleHttp
    // this is most likely due to a network issue
    echo "Could not contact backend endpoint. ", $ex->getMessage();
}


if ($response->isSuccess()) {
    // everything is OK
    $data = $response->getData();

    foreach ($data as $message) {
        /**
         * @var $message \Sendbee\Api\Models\Message
         */
        echo "\n body: ", $message->body;
        echo "\n media_type: ", $message->media_type;
        echo "\n media_url: ", $message->media_url;
        echo "\n status: ", $message->status;
        echo "\n direction: ", $message->direction;
        echo "\n sent_at: ", $message->sent_at;

    }
} else {
    /**
     * @var $error \Sendbee\Api\Transport\ResponseError
     */
    $error = $response->getError();
    if ($error) {
        echo "\n error type: ", $error->type;
        echo "\n error details: ", $error->detail;
    }
}
?>

Fetch conversation messages response:

{
    "data": [
        {
            "body": "...",
            "media_type": "...",
            "media_url": "...",
            "status": "sent|received|delivered|read|failed",
            "direction": "inbound|outbound|template_message|bot",
            "sent_at": "..."
        },
        ...
    ],
    "meta": {
        "current_page": ...,
        "from": ...,
        "to": ...,
        "total": ...,
        "per_page": ...,
        "last_page": ...
    },
    "links": {
        "first": "...",
        "last": "...",
        "next": "...",
        "prev": "..."
    }
}

HTTP Request

GET https://api-v2.sendbee.io/conversations/messages

URI Parameters

Parameter Default Required Description
conversation_id yes Conversation UUID
page 1 Page number for pagination.

Message Type

Type Description
text Text in message
file Document in message (PDF, DOC, DOCX, PPT, PPTX, XLS, XLSX)
image Image in message (JPG/JPEG, PNG)
audio Audio in message (AAC, M4A, AMR, MP3, OGG OPUS)
video Video in message (MP4, 3GPP)

Message Status

Status Description
sent Message is sent but not yet delivered to contact’s device
delivered Message is delivered but not yet read by contact
read Contact read the message
received Contact sent the message and the message has been received to your business number
failed Message failed to be sent

Message Direction

Direction Description
inbound Message is sent by contact
outbound Message is sent by your agent or API and is regular message
template_message Message is sent by your agent or API and is message template
bot Message is sent by your chatbot

Send message

curl -X POST -G "https://api-v2.sendbee.io/conversations/messages/send" \
    -H "X-Auth-Token: ..." \
    -H "X-Api-Key: ..." \
    -H "Accept: application/json" \
    -H "Content-Type": "application/json" \
    -d '{"phone": "...", ...'
from sendbee_api import SendbeeApi

api = SendbeeApi(
    '__your_api_key_here__', '__your_secret_key_here__'
)

response = api.send_message(
    phone='+...', [text='...'], [media_url='...'], [agent_id='...'], [live_inbox=True|False]
)

response.status
response.id
response.message_reference_id
<?php
$data = [
    // phone number to send the message to, MANDATORY
    'phone' => '+...',

    // message text, MANDATORY
    'text' => '...',

    // Media URL for media message
    'media_url' => '',

    // Set to true to disable turning-off chatbot
    'prevent_bot_off' => true|false,

    // trigger real-time events after sending
    'live_inbox => true|false,

    // assigned agent for the conversation
    'agent_id' => '...'
];

try {
    $response = $sendbeeApi->sendMessage($data);
} catch (\Sendbee\Api\Support\DataException $ex) {
    // handle missing data
    // this happens when required data was not provided
    echo "Missing required data. ", $ex->getMessage();
} catch (\Exception $ex) {
    // handle exception thrown by GuzzleHttp
    // this is most likely due to a network issue
    echo "Could not contact backend endpoint. ", $ex->getMessage();
}

if ($response->isSuccess()) {
    /**
     * @var $messageInfo \Sendbee\Api\Models\SentMessage
     */
    $messageInfo = $response->getData();
    // $messageInfo contains message information
    print_r($messageInfo);
} else {
    /**
     * @var $error \Sendbee\Api\Transport\ResponseError
     */
    $error = $response->getError();
    if ($error) {
        // handle error
        echo "\n error type: ", $error->type;
        echo "\n error details: ", $error->detail;
    }
}
?>

Send message response:

{
    "data": {
        "status":  "sent|queued",

        // save this id, and when you get sent message status requests on
        // your webhook, you'll get this same id to identify the conversation
        "id": "__conversation_id__",

        // save this id, and when you get sent message status requests on
        // your webhook, you'll get this same id to identify message
        "message_reference_id": "__message_id__"
    }
}

When a message is sent using API to an existing conversation, it is viewable in the web UI, but the conversation doesn’t appear at the top.

Supported media formats

Category Formats
Audio AAC, M4A, AMR, MP3, OGG OPUS
Video MP4, 3GPP
Image JPG/JPEG, PNG
Document PDF, DOC, DOCX, PPT, PPTX, XLS, XLSX

HTTP Request

POST https://api-v2.sendbee.io/conversations/messages/send

Data Parameters

Parameter Default Required Description
phone yes Contact phone number. Number must be passed in the E.164 format: +/country code/area code/phone number, UK number example: +442071838750.
text Text content of the message or caption if you use media_url.
media_url A publicly accessible url to the media file. The url must serve appropriate Content-Length and Content-Type headers for the file being sent.
prevent_bot_off If there is an active bot running on your Sendbee account, it will be turned off with every message sent using API. Use this parameter to prevent the bot from turning off. Set this parameter to “true”.
agent_id ID of an agent (team member) to whom the conversation will be assigned. Don’t include this parameter if you don’t want to change assigned agent. If the conversation is unassigned, it will stay unassigned if this parameters isn’t included. Learn more how to get an agent ID.
live_inbox true If you want to display a message sent via API in your inbox in real-time, set this parameter to “true”. If the parameter is set to “false” a message will not be displayed and your existing conversation list will remain unchanged.

Whatsapp Message Templates

Fetch message templates

curl -X GET -G "https://api-v2.sendbee.io/conversations/messages/templates" \
    -H "X-Auth-Token: ..." \
    -H "X-Api-Key: ..." \
    -H "Accept: application/json" \
    -H "Content-Type": "application/json"
from sendbee_api import SendbeeApi

api = SendbeeApi(
    '__your_api_key_here__', '__your_secret_key_here__'
)

templates = api.message_templates(
    [status="pending|approved|rejected"], [search_query='...'],
    [page=...], [limit=...]
)

for template in templates:
    template.id
    template.text
    template.tags
    template.buttons # available for all Sendbee users onboarded after 11th of December 2020
    template.button_tags # available for all Sendbee users onboarded after 11th of December 2020
    template.keyword
    template.language
    template.status
    template.rejected_reason
<?php
// optional parameters
$params = [
    'status' => 'pending|approved|rejected', // Fetch approved or unapproved templates
    'search_query' => '', // Filter by query string
    'page' => 1 // Page number for pagination
];

try {
    $response = $sendbeeApi->getMessageTemplates($params);
} catch (\Exception $ex) {
    // handle exception thrown by GuzzleHttp
    // this is most likely due to a network issue
    echo "Could not contact backend endpoint. ", $ex->getMessage();
}

if ($response->isSuccess()) {
    // everything is OK

    $data = $response->getData();

    foreach ($data as $messageTemplate) {
        /**
         * @var $messageTemplate \Sendbee\Api\Models\MessageTemplate
         */
        echo "\n ID: ", $messageTemplate->id;
        echo "\n status: ", $messageTemplate->status;
        echo "\n attachment: ", $messageTemplate->attachment;
        echo "\n keyword: ", $messageTemplate->keyword;
        echo "\n text: ", $messageTemplate->text;
        echo "\n language: ", $messageTemplate->language;
        echo "\n rejected_reason: ", $messageTemplate->rejected_reason;

        foreach ($messageTemplate->tags as $tag) {
            /**
             * @var $tag \Sendbee\Api\Models\MessageTemplateTag
             */
            echo "\n tag -> name: ", $tag->name;
        }
        foreach ($messageTemplate->buttons as $button) {
            /**
             * @var $tag \Sendbee\Api\Models\MessageTemplateButton
             */
            echo "\n button -> index: ", $tag->index;
            echo "\n button -> type: ", $tag->type;
            echo "\n button -> title: ", $tag->title;
            echo "\n button -> value: ", $tag->value;
        }
    }
} else {
    /**
     * @var $error \Sendbee\Api\Transport\ResponseError
     */
    $error = $response->getError();
    if ($error) {
        echo "\n error type: ", $error->type;
        echo "\n error details: ", $error->detail;
    }
}
?>

Fetch message templates response:

{
   "data": [
       {
           "id": "...",
           "text": "...",
           "tags": ["...", ...],
           "buttons": [...], #  available for all Sendbee users onboarded after 11th of December 2020,
           "button_tags": [...], #  available for all Sendbee users onboarded after 11th of December 2020
           "keyword": "...",
           "language": "...",
           "attachment": null|"image"|"video"|"document",
           "status": "pending|approved|rejected",
           "rejected_reason": "..."
       },
       ...
   ],
   "meta": {
       "current_page": ...,
       "from": ...,
       "to": ...,
       "total": ...,
       "per_page": ...,
       "last_page": ...
   },
   "links": {
       "first": "...",
       "last": "...",
       "next": "...",
       "prev": "..."
   }
}

Message templates must be sent to Facebook for approval.
Therefor every message template has status field which can have 3 different values:

Status Description
pending Template is sent for approval and is waiting to be approved or rejected
approved Template is approved and is ready to be used
rejected Template is rejected and cannot be used

If the template is rejected, rejected reason should come in rejected_reason field.
There are a number of reasons why a template can be rejected:

Reason key Description
FORMATTING Formatting is incorrect
COMMERCE_VIOLATION The message template(s) contain content that violates WhatsApp’s Commerce Policy
BUSINESS_VIOLATION The message template(s) contain content that violates WhatsApp’s Business Policy
PROMOTIONAL The message template(s) are considered promotional
ABUSIVE The message template(s) contain potentially abusive or threatening content

Message template can also have attachment.
That means you can send image, video or document URL together with text.
Learn more how to send attachment in Send Message Template section.

HTTP Request

GET https://api-v2.sendbee.io/conversations/messages/templates

URI Parameters

Parameter Default Required Description
status Filter templates by status: “pending” or “approved” or “rejected”
search_query Filter message templates by string. The query will search through keywords and template texts.
page 1 Page number for pagination.
limit 10 Number of items per page. Maximum is 100.

Send message template

curl -X POST -G "https://api-v2.sendbee.io/conversations/messages/templates/send" \
    -H "X-Auth-Token: ..." \
    -H "X-Api-Key: ..." \
    -H "Accept: application/json" \
    -H "Content-Type": "application/json" \
    -d '{"phone": "...", "template_keyword": "...", "language": "...", "tags": {...}}'
from sendbee_api import SendbeeApi

api = SendbeeApi(
    '__your_api_key_here__', '__your_secret_key_here__'
)

response = api.send_template_message(
    phone='+...',
    template_keyword='...',
    language='...', 
    tags={'__tag_key__': '__tag_value__', ...},
    [agent_id='...'], 
    [live_inbox=True|False]
)

response.status
response.id
response.message_reference_id
<?php
$data = [
    // phone number to send the message to, MANDATORY
    'phone' => '+...',

    // keyword of an existing message template you are using, MANDATORY
    'template_keyword' => '...',

    // language code of an existing message template you are using, MANDATORY
    'language' => 'en',

    // tags, key-value pairs of data that is injected in placeholders, MANDATORY
    // example:
    //   message template is 'Your order {{order}} has been dispatched. Please expect delivery by {{date}}'
    //   tags are ['order' => 55, 'date' => '2020-12-12']
    //   final message will be 'Your order 55 has been dispatched. Please expect delivery by 2020-12-12'
    'tags' => [],

    // Set to true to disable turning-off chatbot
    'prevent_bot_off' => true,

    // send attachment url for media template mesages
    'attachment' => 'http...',

    // trigger real-time events after sending
    'live_inbox => true|false,

    // assigned agent for the conversation
    'agent_id' => '...'
];

try {
    $response = $sendbeeApi->sendMessageTemplate($data);
} catch (\Sendbee\Api\Support\DataException $ex) {
    // handle missing data
    // this happens when required data was not provided
    echo "Missing required data. ", $ex->getMessage();
} catch (\Exception $ex) {
    // handle exception thrown by GuzzleHttp
    // this is most likely due to a network issue
    echo "Could not contact backend endpoint. ", $ex->getMessage();
}

if ($response->isSuccess()) {
    /**
     * @var $messageInfo \Sendbee\Api\Models\SentMessage
     */
    $messageInfo = $response->getData();
    // $messageInfo contains message information
    print_r($messageInfo);
} else {
    /**
     * @var $error \Sendbee\Api\Transport\ResponseError
     */
    $error = $response->getError();
    if ($error) {
        // handle error
        echo "\n error type: ", $error->type;
        echo "\n error details: ", $error->detail;
    }
}
?>

Send message template response:

{
    "data": {
        "status":  "sent|queued",

        // save this id, and when you get sent message status requests on
        // your webhook, you'll get this same id to identify the conversation
        "id": "__conversation_id__",

        // save this id, and when you get sent message status requests on
        // your webhook, you'll get this same id to identify message
        "message_reference_id": "__message_id__"
    }
}

When a message template is sent using API to a contact for the first time and using live_inbox=false, it will not be visible in the web UI.
But when a message template is sent using API to an existing conversation and using live_inbox=false, it is viewable in the web UI, but the conversation doesn’t appear at the top.
But if you use live_inbox=true everything is visible in real-time.

Also, you need to be aware when you are sending a WhatsApp message template, you are using someone’s phone number as a destination. If that contact isn’t already in our database, we will save it automatically, but this contact will then be saved with the phone number only and without any other details like name, tags, email, etc.

If you want to update the contact after sending the message, please use this endpoint:
https://developer.sendbee.io/#update-contact

If you want to create the contact before sending the message, please use this endpoint:
https://developer.sendbee.io/#subscribe-contact

HTTP Request

POST https://api-v2.sendbee.io/conversations/messages/templates/send

Data Parameters

Parameter Default Required Description
phone yes Contact phone number. Number must be passed in the E.164 format: +/country code/area code/phone number, UK number example: +442071838750.
template_keyword yes Every pre-created and approved message template is identified with a keyword.
language yes Language keyword, example: en (for english).
tags Message templates use placeholder values (tags) that can be replaced with your custom data inside the curly braces {{…}}. Example: message template: “Welcome {{1}}! How can we help you?”, tags: {“1”: “John”}
button_tags Message template can include dynamic URL button which uses placeholder value (tag) that can be replaced with your custom data inside the curly braces {{…}}.
attachment Media message template supports attachment. Value of this field should be URL of uploaded image, video or document. Number API does not support attachment upload or hosting, you should upload it your self and provide an URL here.
prevent_bot_off If there is an active bot running on your Sendbee account, it will be turned off with every message sent using API. Use this parameter to prevent the bot from turning off. Set this parameter to “true”.
agent_id ID of an agent (team member) to whom the conversation will be assigned. Don’t include this parameter if you don’t want to change assigned agent. If the conversation is unassigned, it will stay unassigned if this parameters isn’t included. Learn more how to get an agent ID.
live_inbox true If you want to display a message sent via API in your inbox in real-time, set this parameter to “true”. If the parameter is set to “false” a message will not be displayed and your existing conversation list will remain unchanged.

Send media message template

curl -X POST -G "https://api-v2.sendbee.io/conversations/messages/templates/send" \
    -H "X-Auth-Token: ..." \
    -H "X-Api-Key: ..." \
    -H "Accept: application/json" \
    -H "Content-Type": "application/json" \
    -d '{"phone": "...", "template_keyword": "...", 
        "language": "...", "tags": {...}, "attachment": "..."}'
from sendbee_api import SendbeeApi

api = SendbeeApi(
    '__your_api_key_here__', '__your_secret_key_here__'
)

response = api.send_template_message(
    phone='+...',
    template_keyword='...',
    language='...', 
    tags={'__tag_key__': '__tag_value__', ...},
    [attachment='...'],
    [agent_id='...']
)

response.status
response.conversation_id
# save this id, and when you get sent message status requests on
# your webhook, you'll get this same id to identify the conversation
<?php
$data = [
    // phone number to send the message to, MANDATORY
    'phone' => '+...',

    // keyword of an existing message template you are using, MANDATORY
    'template_keyword' => '...',

    // language code of an existing message template you are using, MANDATORY
    'language' => 'en',

    // tags, key-value pairs of data that is injected in placeholders, MANDATORY
    // example:
    //   message template is 'Your order {{order}} has been dispatched. Please expect delivery by {{date}}'
    //   tags are ['order' => 55, 'date' => '2020-12-12']
    //   final message will be 'Your order 55 has been dispatched. Please expect delivery by 2020-12-12'
    'tags' => [],

    // Set to true to disable turning-off chatbot
    'prevent_bot_off' => true,

    // send attachment url for media template mesages
    'attachment' => 'http...'
];

try {
    $response = $sendbeeApi->sendMessageTemplate($data);
} catch (\Sendbee\Api\Support\DataException $ex) {
    // handle missing data
    // this happens when required data was not provided
    echo "Missing required data. ", $ex->getMessage();
} catch (\Exception $ex) {
    // handle exception thrown by GuzzleHttp
    // this is most likely due to a network issue
    echo "Could not contact backend endpoint. ", $ex->getMessage();
}

if ($response->isSuccess()) {
    /**
     * @var $messageInfo \Sendbee\Api\Models\SentMessage
     */
    $messageInfo = $response->getData();
    // $messageInfo contains message information
    print_r($messageInfo);
} else {
    /**
     * @var $error \Sendbee\Api\Transport\ResponseError
     */
    $error = $response->getError();
    if ($error) {
        // handle error
        echo "\n error type: ", $error->type;
        echo "\n error details: ", $error->detail;
    }
}
?>

Send message template response:

{
    "data": {
        "status":  "sent|queued",
        "conversation_id": "..."
        // save this id, and when you get sent message status requests on
        // your webhook, you'll get this same id to identify the conversation
    }
}

Media message template can have attachment.
That means you can send image, video or document URL together with text.
When you get a list of message templates, every template in that list has attachment field with it’s value.
Attachment field value defines which type of attachment can be sent with message template:

Value Description
image Message template can be sent with image URL: JPG/JPEG, PNG
video Message template can be sent with video URL: MP4, 3GPP
document Message template can be sent with document URL: PDF, DOC, DOCX, PPT, PPTX, XLS, XLSX
null Message template does not support attachment URL

HTTP Request

POST https://api-v2.sendbee.io/conversations/messages/templates/send

Data Parameters

Parameter Default Required Description
phone yes Contact phone number. Number must be passed in the E.164 format: +/country code/area code/phone number, UK number example: +442071838750.
template_keyword yes Every pre-created and approved message template is identified with a keyword.
language yes Language keyword, example: en (for english).
tags Message templates use placeholder values (tags) that can be replaced with your custom data inside the curly braces {{…}}. Example: message template: “Welcome {{1}}! How can we help you?”, tags: {“1”: “John”}
prevent_bot_off If there is an active bot running on your Sendbee account, it will be turned off with every message sent using API. Use this parameter to prevent the bot from turning off. Set this parameter to “true”.
agent_id ID of an agent (team member) to whom the conversation will be assigned. Don’t include this parameter if you don’t want to change assigned agent. If the conversation is unassigned, it will stay unassigned if this parameters isn’t included. Learn more how to get an agent ID.
attachment Message template supports attachment. Value of this field should be URL of uploaded image, video or document. Sendbee API does not support attachment upload or hosting, you should upload it your self and provide an URL here.

Send interactive message template

curl -X POST -G "https://api-v2.sendbee.io/conversations/messages/templates/send" \
    -H "X-Auth-Token: ..." \
    -H "X-Api-Key: ..." \
    -H "Accept: application/json" \
    -H "Content-Type": "application/json" \
    -d '{"phone": "...", "template_keyword": "...", 
        "language": "...", "tags": {...}, "button_tags": {...}}'
from sendbee_api import SendbeeApi

api = SendbeeApi(
    '__your_api_key_here__', '__your_secret_key_here__'
)

response = api.send_template_message(
    phone='+...',
    template_keyword='...',
    language='...', 
    tags={'__tag_name__': '__tag_value__', ...},
    button_tags={'__tag_name__': '__tag_value__', ...},
    [agent_id='...']
)

response.status
response.conversation_id
# save this id, and when you get sent message status requests on
# your webhook, you'll get this same id to identify the conversation
<?php
$data = [
    // phone number to send the message to, MANDATORY
    'phone' => '+...',

    // keyword of an existing message template you are using, MANDATORY
    'template_keyword' => '...',

    // language code of an existing message template you are using, MANDATORY
    'language' => 'en',

    // tags, key-value pairs of data that is injected in placeholders, MANDATORY
    // example:
    //   message template is 'Your order {{order}} has been dispatched. Please expect delivery by {{date}}'
    //   tags are ['order' => 55, 'date' => '2020-12-12']
    //   final message will be 'Your order 55 has been dispatched. Please expect delivery by 2020-12-12'
    'tags' => [],
    'button_tags' => [],

    // Set to true to disable turning-off chatbot
    'prevent_bot_off' => true,

    // send attachment url for media template mesages
    'attachment' => 'http...'
];

try {
    $response = $sendbeeApi->sendMessageTemplate($data);
} catch (\Sendbee\Api\Support\DataException $ex) {
    // handle missing data
    // this happens when required data was not provided
    echo "Missing required data. ", $ex->getMessage();
} catch (\Exception $ex) {
    // handle exception thrown by GuzzleHttp
    // this is most likely due to a network issue
    echo "Could not contact backend endpoint. ", $ex->getMessage();
}

if ($response->isSuccess()) {
    /**
     * @var $messageInfo \Sendbee\Api\Models\SentMessage
     */
    $messageInfo = $response->getData();
    // $messageInfo contains message information
    print_r($messageInfo);
} else {
    /**
     * @var $error \Sendbee\Api\Transport\ResponseError
     */
    $error = $response->getError();
    if ($error) {
        // handle error
        echo "\n error type: ", $error->type;
        echo "\n error details: ", $error->detail;
    }
}
?>

Send message template response:

{
    "data": {
        "status":  "sent|queued",
        "conversation_id": "..."
        // save this id, and when you get sent message status requests on
        // your webhook, you'll get this same id to identify the conversation
    }
}

Interactive message templates expand the content you can send recipients beyond the standard message template and media messages template types to include interactive buttons.

There are two types of predefined buttons offered:

Value Description
Call-to-Action Allows your customer to call a phone number or visit a website.
Quick Reply Allows your customer to return a simple text message.

These buttons can be attached to text message templates or media message templates.
Once your interactive message templates have been created and approved, you can use them in notification messages as well as customer service/care messages.

This is an example of message template with quick reply buttons:
Quick reply buttons

When your customer clicks on a quick reply button, a response is sent.
A customer may not click a button and either reply to the interactive message or just send you a message.

This is an example of message template with call-to-action buttons:
Call-to-action buttons

Call-to-action button can have dynamic URL suffix: https://example.com/{{1}}
{{1}} is a placeholder which can be replaced with the last part of the URL: page/123
Finally the result is: https://example.com/page/123

Call-to-action button dynamic URL suffix works the same way as message template tag.
They are both placeholder which can be replaced with an actual content.
Use button_tags API parameter to replace dynamic suffix in call-to-action URL button.

HTTP Request

POST https://api-v2.sendbee.io/conversations/messages/templates/send

Data Parameters

Parameter Default Required Description
phone yes Contact phone number. Number must be passed in the E.164 format: +/country code/area code/phone number, UK number example: +442071838750.
template_keyword yes Every pre-created and approved message template is identified with a keyword.
language yes Language keyword, example: en (for english).
tags Message templates use placeholder values (tags) that can be replaced with your custom data inside the curly braces {{…}}. Example: message template: “Welcome {{1}}! How can we help you?”, tags: {“1”: “John”}
button_tags If you’re using call to action URL buttons in message templates with dynamic suffix use placeholder values (tags) that can be replaced with your custom data inside the curly braces {{…}}. Example: call to action URL button: “https://example.com/{{1}}”, button_tags: {“1”: “page/123?param=value”}
prevent_bot_off If there is an active bot running on your Sendbee account, it will be turned off with every message sent using API. Use this parameter to prevent the bot from turning off. Set this parameter to “true”.
agent_id ID of an agent (team member) to whom the conversation will be assigned. Don’t include this parameter if you don’t want to change assigned agent. If the conversation is unassigned, it will stay unassigned if this parameters isn’t included. Learn more how to get an agent ID.

Automation

The Sendbee API gives you the ability to manage the status settings of automated replies (chatbot). More automation features to come in the future.

Automated replies status check

curl -X GET -G "https://api-v2.sendbee.io/automation/chatbot/activity/status?conversation_id=..." \
    -H "X-Auth-Token: ..." \
    -H "X-Api-Key: ..." \
    -H "Accept: application/json" \
    -H "Content-Type": "application/json"
from sendbee_api import SendbeeApi

api = SendbeeApi(
    '__your_api_key_here__', '__your_secret_key_here__'
)

api.chatbot_activity_status(conversation_id='...')
<?php
$data = [
    // conversation_id, MANDATORY
    'conversation_id' => '...'
];

try {
    $response = $sendbeeApi->getChatbotActivity($data);
} catch (\Sendbee\Api\Support\DataException $ex) {
    // handle missing data
    // this happens when required data was not provided
    echo "Missing required data. ", $ex->getMessage();
} catch (\Exception $ex) {
    // handle exception thrown by GuzzleHttp
    // this is most likely due to a network issue
    echo "Could not contact backend endpoint. ", $ex->getMessage();
}

if ($response->isSuccess()) {
    /**
     * @var $status \Sendbee\Api\Models\ChatbotStatus
     */
    $status = $response->getData();

    echo "\n conversation_id: ", $status->text;
    echo "\n chatbot_active: ", $status->chatbot_active ? 'ON' : 'OFF';

} else {
    /**
     * @var $error \Sendbee\Api\Transport\ResponseError
     */
    $error = $response->getError();
    if ($error) {
        // handle error
        echo "\n error type: ", $error->type;
        echo "\n error details: ", $error->detail;
    }
}
?>

Chatbot status settings response:

{
    "data": {
        "conversation_id": "...",
        "chatbot_status": true|false
    }
}

Automated replies (chatbot) are messages that would be automatically triggered based on the keywords that were received in the inbound message. Automated replies can be created in the Sendbee Dashboard.

Use automated replies status check to check if the bot for the conversation is active or not. Every contact is linked to a conversation. Automated replies would automatically turn OFF if a message was sent to a contact by an agent via the Sendbee Dashboard or via the API unless prevent_bot_off parameter is used.

You can turn the chatbot on or off for a certain conversation.

HTTP Request

GET https://api-v2.sendbee.io/automation/chatbot/activity/status

URI Parameters

Parameter Default Required Description
conversation_id yes Conversation UUID

Automated replies status settings

curl -X PUT -G "https://api-v2.sendbee.io/automation/chatbot/activity" \
    -H "X-Auth-Token: ..." \
    -H "X-Api-Key: ..." \
    -H "Accept: application/json" \
    -H "Content-Type": "application/json" \
    -d '{"conversation_id": "...", "active": true|false}'
from sendbee_api import SendbeeApi

api = SendbeeApi(
    '__your_api_key_here__', '__your_secret_key_here__'
)

response = api.chatbot_activity(conversation_id='...', active=True|False)

response.conversation_id
response.chatbot_active # True/False
<?php
$sendbeeApi = new \Sendbee\Api\Client($public_key, $secret);

$data = [
    // conversation_id, MANDATORY
    'conversation_id' => '...',
    // boolean value, true to enable chatbot, false to disable, MANDATORY
    'active' => true | false
];

try {
    $response = $sendbeeApi->chatbotActivity($data);
} catch (\Sendbee\Api\Support\DataException $ex) {
    // handle missing data
    // this happens when required data was not provided
    echo "Missing required data. ", $ex->getMessage();
} catch (\Exception $ex) {
    // handle exception thrown by GuzzleHttp
    // this is most likely due to a network issue
    echo "Could not contact backend endpoint. ", $ex->getMessage();
}

if ($response->isSuccess()) {
    /**
     * @var $tag \Sendbee\Api\Models\ServerMessage
     */
    $message = $response->getData();
    // chatbot activity is now set
    // $message contains server info message
    print_r($message);

} else {
    /**
     * @var $error \Sendbee\Api\Transport\ResponseError
     */
    $error = $response->getError();
    if ($error) {
        // handle error
        echo "\n error type: ", $error->type;
        echo "\n error details: ", $error->detail;
    }
}
?>

Chatbot status settings response:

{
    "data": {
        "message": "Chatbot turned on/off"
    }
}

Automated replies (chatbot) are messages that would be automatically triggered based on the keywords that were received in the inbound message. Automated replies can be created in the Sendbee Dashboard.

Use automated replies status settings to turn automated replies ON or OFF for a certain conversation. Every contact is linked to a conversation. Automated replies would automatically turn OFF if a message was sent to a contact by an agent via the Sendbee Dashboard or via the API unless prevent_bot_off parameter is used.

You can turn the chatbot on or off for a certain conversation.

HTTP Request

PUT https://api-v2.sendbee.io/automation/chatbot/activity

Data Parameters

Parameter Default Required Description
conversation_id yes Conversation UUID
active yes True of False

Webhooks

Webhook request headers section:

{
    "X-Auth-Token": "...",
    "Accept": "application/json",
    "Content-Type": "application/json"
}

A webhook allows you to be automatically notified when something happens in Sendbee without having to constantly poll the API. Webhooks can be activated in the Sendbee Dashboard.

Supported webhooks:

Keyword Category Description
contact.created Contacts New contact has been created
contact.subscription_created Contacts New contact has been subscribed
contact.subscription_updated Contacts Contact subscription has been updated
contact.subscription_unsubscribed Contacts Contact unsubscribed from receiving messages
message.received Messages Message has been received from WhatsApp contact
message.sent Messages Message has been sent by agent or API
message.sent_status Messages Message has been updated with a new status (delivered or read)
whatsapp.message.template.sent_status WhatsApp WhatsApp template message has been updated with a new status (delivered or read)
whatsapp.message.session.sent_status WhatsApp WhatsApp session message has been updated with a new status (delivered or read)
whatsapp.message.template.approval_status WhatsApp WhatsApp message template approval status has been updated (delivered or read)

Every request to a webhook URL consists of body and headers elements.

Webhook authentication


from sendbee_api import SendbeeApi

api = SendbeeApi(
    '__your_api_key_here__', '__your_secret_key_here__'
)

token = '...'  # taken from the request header
if not api.auth.check_auth_token(token):
    # error! authentication failed!
<?php
// Your API secret
$API_SECRET = '...';
// Received token
$token_from_request_header = '...';

// preferred method: using PHP API
$requestIsValid = \Sendbee\Api\Client::verifyToken($API_SECRET, $token_from_request_header);


// manual method (if you don't want to use the PHP API library)
function verifySendbeeToken($apiSecret, $token, $expirationSeconds = 0)
{
    // sanity check - token should be a non-empty string
    if(!is_string($token) || !$token)
    {
        return false;
    }

    // step 1: decode token using base64 encoding
    $decoded = base64_decode($token);

    // step 2: split encoded string by dot (.) to get timestamp (left part)
    // separated from encrypted string (right part)
    $parts = explode('.', $decoded);

    // sanity check - there should be 2 values
    if(count($parts) != 2)
    {
        return false;
    }

    $receivedTimestamp = $parts[0];
    $receivedHash = $parts[1];

    if($expirationSeconds < 1)
    {
        // set how old can the token be to 15 minutes
        // (or whatever period you think is acceptable for you)
        $expirationSeconds = 60 * 15;
    }

    // step 3: check if the timestamp is not older then specified $expirationSeconds
    if($receivedTimestamp < (time() - $expirationSeconds))
    {
        return false;
    }

    // step 4: encrypt timestamp with your API secret using hmac sha256 and get a hash
    $hash = hash_hmac('sha256', $receivedTimestamp, $apiSecret, false);

    // step 5: compare the hash we received with the one we generated in step 2
    // they need to be equal
    return ($hash == $receivedHash);
}

$requestIsValid = verifySendbeeToken($API_SECRET, $token_from_request_header);

?>

It is highly recommended to authorize every request that Sendbee sends to a webhook, just to be sure that the request indeed is coming from Sendbee on behalf of your account.
To authorize a request coming from Sendbee, use X-Auth-Token token in headers of the request.

To check if the token is valid, please follow these steps:
1. decode token using base64 encoding
2. split encoded string by dot (.) to get timestamp (left part) separated from encrypted string (right part)
3. check if the timestamp is not older then 15 minutes (or whatever period you think is acceptable for you)
5. encrypt timestamp from split string (step 2) with your API secret using hmac sha256
6. compare this encrypted timestamp with encrypted part (step 2), they need to be equal

contact.created

Request body for contact.created

{
    "business": {
        "id": "...",
        "name": "...",
    },
    "contact": {
        "created_at": 123456789,
        "id": "...",
        "name": "...",
        "phone": "...",
    },
    "type": "contact.created",
}

Every time a contact is created Sendbee sends a request to your webhook URL.
Contact can be created in various ways:
- Contact sends you a message
- Contact subscribes for receiving messages via the custom opt-in source
- You subscribe a contact via Sendbee API

contact.subscription_created

Request body for contact.subscription_created

{
    "business": {
        "id": "...",
        "name": "...",
    },
    "contact": {
        "created_at": 123456789,
        "id": "...",
        "name": "...",
        "phone": "...",
    },
    "subscribed_at": 123456789,
    "subscribed_via": "API",
    "subscription_updated_at": 123456789,
    "type": "contact.subscription_created",
}

Every time a contact subscribes Sendbee sends a request to your webhook URL.
Contact can be subscribed in various ways:
- Contact subscribes for receiving messages via the custom opt-in source
- You subscribe a contact via Sendbee API

contact.subscription_updated

Request body for contact.subscription_updated

{
    "business": {
        "id": "...",
        "name": "...",
    },
    "contact": {
        "created_at": 123456789,
        "id": "...",
        "name": "...",
        "phone": "...",
    },
    "subscribed_at": 123456789,
    "subscribed_via": "API",
    "subscription_updated_at": 123456789,
    "type": "contact.subscription_updated",
}

Every time a contact re-subscribes Sendbee sends a request to your webhook URL. Contact can be re-subscribed in various ways:
- Existing contact subscribes for receiving messages via the custom opt-in source
- You subscribe an existing contact via Sendbee API

contact.unsubscribed

Request body for contact.unsubscribed

{
    "business": {
        "id": "...",
        "name": "...",
    },
    "contact": {
        "created_at": 123456789,
        "id": "...",
        "name": "...",
        "phone": "...",
    },
    "type": "contact.unsubscribed",
}

Every time a contact unsubscribes from receiving notifications by sending a STOP keyword to your business, Sendbee will send a request to your webhook URL.

message.received

Request body for message.received (text message)

{
    "business": {
        "id": "...",
        "name": "...",
    },
    "contact": {
        "created_at": 123456789,
        "id": "...",
        "name": "...",
        "phone": "...",
    },
    "channel": "whatsapp",
    "content": {
        "text": "...",
    },
    "content_type": "text",
    "conversation_id": "...",
    "destination": "...", // your business phone number
    "direction": "inbound",
    "error": null,
    "sent_at": 123456789,
    "source": "...", // contact phone number
    "type": "message.received",
}

Request body for message.received (media message)

{
    "business": {
        "id": "...",
        "name": "...",
    },
    "contact": {
        "created_at": 123456789,
        "id": "...",
        "name": "...",
        "phone": "...",
    },
    "channel": "whatsapp",
    "content": {
        "media": {
            "caption": '',
            "type": "image|video|document",
            "url": "...",
        },
    },
    "content_type": "media",
    "conversation_id": "...",
    "destination": "...", // your business phone number
    "direction": "inbound",
    "error": null,
    "sent_at": 123456789,
    "source": "...", // contact phone number
    "type": "message.received",
}

Every time a contact sends a message to your business, Sendbee will send a message to your webhook URL.

message.sent

Request body for message.sent (text message)

{
    "business": {
        "id": "...",
        "name": "...",
    },
    "contact": {
        "created_at": 123456789,
        "id": "...",
        "name": "...",
        "phone": "...",
    },
    "channel": "whatsapp",
    "content": {
        "text": "...",
    },
    "content_type": "text",
    "conversation_id": "...",
    "destination": "...", // contact phone number
    "direction": "inbound",
    "error": null,
    "sent_at": 123456789,
    "source": "...", // your business phone number
    "type": "message.sent",
}

Request body for message.sent (media message)

{
    "business": {
        "id": "...",
        "name": "...",
    },
    "contact": {
        "created_at": 123456789,
        "id": "...",
        "name": "...",
        "phone": "...",
    },
    "channel": "whatsapp",
    "content": {
        "media": {
            "caption": '',
            "type": "image|video|document",
            "url": "...",
        },
    },
    "content_type": "media",
    "conversation_id": "...",
    "destination": "...", // contact phone number
    "direction": "inbound",
    "error": null,
    "sent_at": 123456789,
    "source": "...", // your business phone number
    "type": "message.sent",
}

Every time your agent sends a message to contact, or a message has been sent using API, Sendbee will send a message to your webhook URL.

message.sent_status

Request body for message.sent_status

{
    "business": {
        "id": "...",
        "name": "...",
    },
    "contact": {
        "created_at": 123456789,
        "id": "...",
        "name": "...",
        "phone": "...",
    },
    "channel": "whatsapp",
    "content": {...},
    "content_type": "...",
    "conversation_id": "...",
    "message_id": "...",
    "destination": "...", // contact phone number
    "direction": "outbound",
    "error": null,
    "sent_at": "2020-01-01 12:00:00",
    "source": "...", // your business phone number
    "status": "sent|delivered|read",
    "type": "message.sent_status",
}

Every time your business sends a message to a contact, Sendbee will send a message to your webhook URL indicating the status of the sent message.

whatsapp.message.template.sent_status

Request body for whatsapp.message.template.sent_status

{
    "business": {
        "id": "...",
        "name": "...",
    },
    "contact": {
        "created_at": 123456789,
        "id": "...",
        "name": "...",
        "phone": "...",
    },
    "channel": "whatsapp",
    "content": {...},
    "content_type": "...",
    "conversation_id": "...",
    "message_id": "...",
    "destination": "...", // contact phone number
    "direction": "outbound",
    "error": null,
    "sent_at": "2020-01-01 12:00:00",
    "source": "...", // your business phone number
    "status": "sent|delivered|read",
    "type": "whatsapp.message.template.sent_status",
}

Every time your business sends a WhatsApp template message to a contact, Sendbee will send a message to your webhook URL indicating the status of the sent message.

whatsapp.message.session.sent_status

Request body for whatsapp.message.session.sent_status

{
    "business": {
        "id": "...",
        "name": "...",
    },
    "contact": {
        "created_at": 123456789,
        "id": "...",
        "name": "...",
        "phone": "...",
    },
    "channel": "whatsapp",
    "content": {...},
    "content_type": "...",
    "conversation_id": "...",
    "message_id": "...",
    "destination": "...", // contact phone number
    "direction": "outbound",
    "error": null,
    "sent_at": "2020-01-01 12:00:00",
    "source": "...", // your business phone number
    "status": "sent|delivered|read",
    "type": "whatsapp.message.session.sent_status",
}

Every time your business sends a WhatsApp session message to a contact, Sendbee will send a message to your webhook URL indicating the status of the sent message.

whatsapp.message.template.approval_status

Request body for whatsapp.message.template.approval_status

{
    "template_id": "...",
    "keyword": "...",
    "status": "approved|rejected",
    "type": "whatsapp.message.template.approval_status",
}

Every time your pending WhatsApp message template changes its approval state into approved or rejected, Sendbee will send a message to your webhook URL indicating the new state of your template.

https://app.dev.sendbee.io/oauth/flow? response_type=code& redirect_uri=https%3A%2F%2Fapp.frontapp.com%2Faccounts%2Foauth%2Fexternal%2F62162287& state=e2c00000e527df89b04012f8f4cd1c187fd0& client_id=XVKk6lRWzwN1NhGnZcz6rxvHGO9l7murEJYD3nRm https://dashboard-api.sendbee.io/oauth/flow?response_type=code&redirect_uri=https%3A%2F%2Fapp.frontapp.com%2Faccounts%2Foauth%2Fexternal%2F1479104690&state=e2c000002b4f282ab11f5db2631952ae65ac&client_id=d1Gp7lTc7A5sciGgH3CpBvUnmtVy7ufg5EcGXLaL