Reference
Channel Integrations
Per-location credentials for outbound channels (WhatsApp, Facebook Messenger, Instagram DM, Google My Business). Credentials are encrypted at rest. The OAuth callback endpoint that exchanges authorization codes for tokens is intentionally excluded from public docs (internal redirect target).
List channel integrations
Lists the channel integrations of this location — WhatsApp, email, SMS — newest first. Filter by location, channel, or status.
GET
/api/channel-integrations
Query parameters
-
location_idintegerScope to a location.
Example:
1 -
channelstringFilter by channel. One of
whatsapp,messenger,instagram,gmb.Example:
whatsapp -
statusstringFilter by status (e.g.
active,disconnected,expired).Example:
active -
per_pageintegerRows per page. Default 20, maximum 100. See Lists, paging and filters.
Example:
20
curl --request GET \
--get "https://klozzo.com/api/channel-integrations?location_id=1&channel=whatsapp&status=active&per_page=20" \
--header "Authorization: Bearer {YOUR_API_TOKEN}" \
--header "Content-Type: application/json" \
--header "Accept: application/json"
const url = new URL(
"https://klozzo.com/api/channel-integrations"
);
const params = {
"location_id": "1",
"channel": "whatsapp",
"status": "active",
"per_page": "20",
};
Object.keys(params)
.forEach(key => url.searchParams.append(key, params[key]));
const headers = {
"Authorization": "Bearer {YOUR_API_TOKEN}",
"Content-Type": "application/json",
"Accept": "application/json",
};
fetch(url, {
method: "GET",
headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://klozzo.com/api/channel-integrations';
$response = $client->get(
$url,
[
'headers' => [
'Authorization' => 'Bearer {YOUR_API_TOKEN}',
'Content-Type' => 'application/json',
'Accept' => 'application/json',
],
'query' => [
'location_id' => '1',
'channel' => 'whatsapp',
'status' => 'active',
'per_page' => '20',
],
]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import requests
import json
url = 'https://klozzo.com/api/channel-integrations'
params = {
'location_id': '1',
'channel': 'whatsapp',
'status': 'active',
'per_page': '20',
}
headers = {
'Authorization': 'Bearer {YOUR_API_TOKEN}',
'Content-Type': 'application/json',
'Accept': 'application/json'
}
response = requests.request('GET', url, headers=headers, params=params)
response.json()
{
"data": [
{
"id": null,
"location_id": null,
"channel": "whatsapp",
"status": "connected",
"metadata": {
"account_id": "acct_g2gh67JMFZ"
},
"connected_at": "2026-08-26T02:19:50+00:00",
"disconnected_at": null,
"expires_at": "2026-08-26T04:19:50+00:00",
"created_at": null
},
{
"id": null,
"location_id": null,
"channel": "whatsapp",
"status": "connected",
"metadata": {
"account_id": "acct_ElVYf4Z2Vn"
},
"connected_at": "2026-08-26T02:19:50+00:00",
"disconnected_at": null,
"expires_at": "2026-08-26T04:19:50+00:00",
"created_at": null
}
],
"links": {
"first": "/?page=1",
"last": "/?page=1",
"prev": null,
"next": null
},
"meta": {
"current_page": 1,
"from": 1,
"last_page": 1,
"links": [
{
"url": null,
"label": "« Previous",
"page": null,
"active": false
},
{
"url": "/?page=1",
"label": "1",
"page": 1,
"active": true
},
{
"url": null,
"label": "Next »",
"page": null,
"active": false
}
],
"path": "/",
"per_page": 20,
"to": 2,
"total": 2
}
}
-
dataobject[]-
idstring -
location_idstring -
channelstring -
statusstring -
metadataobject-
account_idstring
-
-
connected_atstring -
disconnected_atstring -
expires_atstring -
created_atstring
-
-
linksobject-
firststring -
laststring -
prevstring -
nextstring
-
-
metaobject-
current_pageinteger -
frominteger -
last_pageinteger -
linksobject[]-
urlstring -
labelstring -
pagestring -
activeboolean
-
-
pathstring -
per_pageinteger -
tointeger -
totalinteger
-
Connect a channel integration
Stores encrypted credentials and marks the integration active. For OAuth
flows, prefer letting the user complete the in-product OAuth dance; use
this endpoint when you already have tokens (e.g. via your own OAuth client).
POST
/api/channel-integrations
Only locations you belong to are accepted; omit location_id to use your
current one. WhatsApp is additionally capped at the account's maximum
number of linked numbers (10 by default).
Body parameters
-
channelstring requiredOne of
whatsapp,messenger,instagram,gmb.Example:
whatsapp -
location_idintegerA location you are a member of. Defaults to your current location.
Example:
1 -
credentialsobject requiredProvider credentials. Must include
access_token.-
access_tokenstring requiredLong-lived access token.
Example:
architecto -
refresh_tokenstringOptional refresh token.
Example:
architecto
-
-
metadataobjectProvider-specific metadata (e.g.
{ "phone_number_id": "..." }for WhatsApp).Example:
[] -
expires_inintegerSeconds until access token expires. Used to compute
expires_at.Example:
3600
curl --request POST \
"https://klozzo.com/api/channel-integrations" \
--header "Authorization: Bearer {YOUR_API_TOKEN}" \
--header "Content-Type: application/json" \
--header "Accept: application/json" \
--data "{
\"channel\": \"whatsapp\",
\"location_id\": 1,
\"credentials\": {
\"access_token\": \"architecto\",
\"refresh_token\": \"architecto\"
},
\"metadata\": [],
\"expires_in\": 3600
}"
const url = new URL(
"https://klozzo.com/api/channel-integrations"
);
const headers = {
"Authorization": "Bearer {YOUR_API_TOKEN}",
"Content-Type": "application/json",
"Accept": "application/json",
};
let body = {
"channel": "whatsapp",
"location_id": 1,
"credentials": {
"access_token": "architecto",
"refresh_token": "architecto"
},
"metadata": [],
"expires_in": 3600
};
fetch(url, {
method: "POST",
headers,
body: JSON.stringify(body),
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://klozzo.com/api/channel-integrations';
$response = $client->post(
$url,
[
'headers' => [
'Authorization' => 'Bearer {YOUR_API_TOKEN}',
'Content-Type' => 'application/json',
'Accept' => 'application/json',
],
'json' => [
'channel' => 'whatsapp',
'location_id' => 1,
'credentials' => ['access_token' => 'architecto', 'refresh_token' => 'architecto'],
'metadata' => [],
'expires_in' => 3600,
],
]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import requests
import json
url = 'https://klozzo.com/api/channel-integrations'
payload = {
"channel": "whatsapp",
"location_id": 1,
"credentials": {
"access_token": "architecto",
"refresh_token": "architecto"
},
"metadata": [],
"expires_in": 3600
}
headers = {
'Authorization': 'Bearer {YOUR_API_TOKEN}',
'Content-Type': 'application/json',
'Accept': 'application/json'
}
response = requests.request('POST', url, headers=headers, json=payload)
response.json()
{
"data": {
"id": null,
"location_id": null,
"channel": "whatsapp",
"status": "connected",
"metadata": {
"account_id": "acct_ZmVG5xkxyX"
},
"connected_at": "2026-08-26T02:19:50+00:00",
"disconnected_at": null,
"expires_at": "2026-08-26T04:19:50+00:00",
"created_at": null
}
}
-
dataobject-
idstring -
location_idstring -
channelstring -
statusstring -
metadataobject-
account_idstring
-
-
connected_atstring -
disconnected_atstring -
expires_atstring -
created_atstring
-
{
"message": "Channel no es una opción válida.",
"errors": {
"channel": [
"Channel no es una opción válida."
]
}
}
Every error shares the same shape — message and errors.
See Errors.
{
"message": "Falta credentials.access_token.",
"errors": {
"credentials.access_token": [
"Falta credentials.access_token."
]
}
}
Every error shares the same shape — message and errors.
See Errors.
{
"message": "Location id no es válido.",
"errors": {
"location_id": [
"Location id no es válido."
]
}
}
Every error shares the same shape — message and errors.
See Errors.
Fetch a channel integration
Returns one channel integration by id. Credentials come back in encrypted/redacted form; only metadata and status are usable client-side.
GET
/api/channel-integrations/{channelIntegration_id}
Path parameters
-
channelIntegration_idinteger requiredIntegration ID.
Example:
4
curl --request GET \
--get "https://klozzo.com/api/channel-integrations/4" \
--header "Authorization: Bearer {YOUR_API_TOKEN}" \
--header "Content-Type: application/json" \
--header "Accept: application/json"
const url = new URL(
"https://klozzo.com/api/channel-integrations/4"
);
const headers = {
"Authorization": "Bearer {YOUR_API_TOKEN}",
"Content-Type": "application/json",
"Accept": "application/json",
};
fetch(url, {
method: "GET",
headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://klozzo.com/api/channel-integrations/4';
$response = $client->get(
$url,
[
'headers' => [
'Authorization' => 'Bearer {YOUR_API_TOKEN}',
'Content-Type' => 'application/json',
'Accept' => 'application/json',
],
]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import requests
import json
url = 'https://klozzo.com/api/channel-integrations/4'
headers = {
'Authorization': 'Bearer {YOUR_API_TOKEN}',
'Content-Type': 'application/json',
'Accept': 'application/json'
}
response = requests.request('GET', url, headers=headers)
response.json()
{
"data": {
"id": null,
"location_id": null,
"channel": "whatsapp",
"status": "connected",
"metadata": {
"account_id": "acct_yB1hXM3BMo"
},
"connected_at": "2026-08-26T02:19:50+00:00",
"disconnected_at": null,
"expires_at": "2026-08-26T04:19:50+00:00",
"created_at": null
}
}
-
dataobject-
idstring -
location_idstring -
channelstring -
statusstring -
metadataobject-
account_idstring
-
-
connected_atstring -
disconnected_atstring -
expires_atstring -
created_atstring
-
{
"message": "No query results for model."
}
Every error shares the same shape — message and errors.
See Errors.
Disconnect a channel integration
Marks the integration disconnected and clears stored credentials. Existing
conversations remain readable; outbound messages on this channel will fail
until reconnected.
DELETE
/api/channel-integrations/{channelIntegration_id}
Path parameters
-
channelIntegration_idinteger requiredIntegration ID.
Example:
4
curl --request DELETE \
"https://klozzo.com/api/channel-integrations/4" \
--header "Authorization: Bearer {YOUR_API_TOKEN}" \
--header "Content-Type: application/json" \
--header "Accept: application/json"
const url = new URL(
"https://klozzo.com/api/channel-integrations/4"
);
const headers = {
"Authorization": "Bearer {YOUR_API_TOKEN}",
"Content-Type": "application/json",
"Accept": "application/json",
};
fetch(url, {
method: "DELETE",
headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://klozzo.com/api/channel-integrations/4';
$response = $client->delete(
$url,
[
'headers' => [
'Authorization' => 'Bearer {YOUR_API_TOKEN}',
'Content-Type' => 'application/json',
'Accept' => 'application/json',
],
]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import requests
import json
url = 'https://klozzo.com/api/channel-integrations/4'
headers = {
'Authorization': 'Bearer {YOUR_API_TOKEN}',
'Content-Type': 'application/json',
'Accept': 'application/json'
}
response = requests.request('DELETE', url, headers=headers)
response.json()
{
"message": "Channel integration disconnected."
}
-
messagestring
Refresh integration tokens
Exchanges the stored refresh token for a new access token at the provider.
Updates expires_at and status. Returns 502 with the provider's error
message when the refresh call fails (typically expired refresh token —
the user must re-OAuth in product).
POST
/api/channel-integrations/{channelIntegration_id}/refresh
Path parameters
-
channelIntegration_idinteger requiredIntegration ID.
Example:
4
curl --request POST \
"https://klozzo.com/api/channel-integrations/4/refresh" \
--header "Authorization: Bearer {YOUR_API_TOKEN}" \
--header "Content-Type: application/json" \
--header "Accept: application/json"
const url = new URL(
"https://klozzo.com/api/channel-integrations/4/refresh"
);
const headers = {
"Authorization": "Bearer {YOUR_API_TOKEN}",
"Content-Type": "application/json",
"Accept": "application/json",
};
fetch(url, {
method: "POST",
headers,
}).then(response => response.json());
$client = new \GuzzleHttp\Client();
$url = 'https://klozzo.com/api/channel-integrations/4/refresh';
$response = $client->post(
$url,
[
'headers' => [
'Authorization' => 'Bearer {YOUR_API_TOKEN}',
'Content-Type' => 'application/json',
'Accept' => 'application/json',
],
]
);
$body = $response->getBody();
print_r(json_decode((string) $body));
import requests
import json
url = 'https://klozzo.com/api/channel-integrations/4/refresh'
headers = {
'Authorization': 'Bearer {YOUR_API_TOKEN}',
'Content-Type': 'application/json',
'Accept': 'application/json'
}
response = requests.request('POST', url, headers=headers)
response.json()
{
"data": {
"id": null,
"location_id": null,
"channel": "whatsapp",
"status": "connected",
"metadata": {
"account_id": "acct_sKkxJlPy6M"
},
"connected_at": "2026-08-26T02:19:50+00:00",
"disconnected_at": null,
"expires_at": "2026-08-26T04:19:50+00:00",
"created_at": null
}
}
-
dataobject-
idstring -
location_idstring -
channelstring -
statusstring -
metadataobject-
account_idstring
-
-
connected_atstring -
disconnected_atstring -
expires_atstring -
created_atstring
-
{
"message": "Refresh token rejected by provider"
}
Every error shares the same shape — message and errors.
See Errors.