ChatKomodo Docs Home Sign in

API reference · Integrations

API keys

Child keys for separate integrations, each with its own request limit and optional device scope. Managed from a dashboard login.

List API keys

GET/v1/api-keys

Lists the tenant's primary key and its child keys (primary first, then newest first, max 1 000), revoked ones included. Only the 8-character prefix is returned, never the key itself. Readable with an API key as well as from a dashboard login.

Responses

  • 200Keys
    Response fields
    • api_keysarray of objectrequired
    • api_keys[].idstringrequired
    • api_keys[].namestringrequired
    • api_keys[].prefixstringrequired
      First 8 characters of the key, safe to display. The full key is only returned once, when it is created
    • api_keys[].is_primarybooleanrequired
      true for the tenant's own key, false for a child key created with POST /v1/api-keys
    • api_keys[].rate_limit_per_minuteintegerrequired
    • api_keys[].created_atintegerrequired
      Unix time in milliseconds
    • api_keys[].last_used_atinteger | nullrequired
      Unix time in milliseconds of the last authenticated request (updated at most once a minute), or null if never used
    • api_keys[].revoked_atinteger | nullrequired
      When the key was revoked, or null while it is active
  • 401Missing, invalid or expired credentials
  • 403Forbidden for a customer-service agent login (Agent access is not allowed for this operation) and an API key restricted to specific sessions (KEY_SCOPE_FORBIDDEN)
Request
curl "https://api.chatkomodo.com/v1/api-keys" \
  -H "Authorization: Bearer $CHATKOMODO_API_KEY"
const res = await fetch('https://api.chatkomodo.com/v1/api-keys', {
  method: 'GET',
  headers: {
    Authorization: `Bearer ${process.env.CHATKOMODO_API_KEY}`,
  },
});
const data = await res.json();
if (!res.ok) throw new Error(`${data.code}: ${data.error}`);
console.log(data);
import os
import requests

res = requests.get(
    "https://api.chatkomodo.com/v1/api-keys",
    headers={
        "Authorization": f"Bearer {os.environ['CHATKOMODO_API_KEY']}",
    },
    timeout=30,
)
res.raise_for_status()
print(res.json())
<?php
require 'vendor/autoload.php';

$client = new GuzzleHttp\Client();
$res = $client->request('GET', 'https://api.chatkomodo.com/v1/api-keys', [
    'headers' => [
        'Authorization' => 'Bearer ' . getenv('CHATKOMODO_API_KEY'),
    ],
]);

print_r(json_decode((string) $res->getBody(), true));
Response 200
{
  "api_keys": [
    {
      "id": "01JA5V6Q8M2H7G4K9P3T0R1S2X",
      "name": "Shop integration",
      "prefix": "string",
      "is_primary": true,
      "rate_limit_per_minute": 50,
      "created_at": 1767225600000,
      "last_used_at": 1767225600000,
      "revoked_at": 1767225600000
    }
  ]
}

Create a child API key

POST/v1/api-keys

Creates a child key that acts as the tenant (same devices, quota, plan and settings) but has its own per-minute request limit and can be revoked on its own. The full key is returned once, in this response only. Needs a dashboard login: an API key cannot mint keys. With the platform admin secret the new key is a separate standalone tenant on the Free plan instead.

Request body

  • namestring
    Label, 1-64 characters (default unnamed)
    1–64 characters
  • rate_limit_per_minuteinteger
    Requests per minute this key may make, across all /v1 endpoints (1-10 000, default 300). Capped to the plan ceiling: 5 times the plan's send rate per minute, at least 300 (600 on Pro with the default send rates)
    From 1 to 10 000

Responses

  • 201Key created
    Response fields
    • idstringrequired
    • keystringrequired
      The secret API key. Shown only in this response; store it securely
    • prefixstringrequired
      First 8 characters of the key
    • namestringrequired
  • 400Body is not valid JSON (INVALID_JSON) or fails validation (VALIDATION_FAILED)
  • 401Missing, invalid or expired credentials
  • 403Forbidden. KEY_MANAGEMENT_REQUIRES_LOGIN when the request was authenticated with an API key (keys can only be managed from a dashboard login). Also refused, without a code, for a customer-service agent login, and with KEY_SCOPE_FORBIDDEN for a session-restricted key
Request
curl -X POST "https://api.chatkomodo.com/v1/api-keys" \
  -H "Authorization: Bearer $CHATKOMODO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "name": "Shop integration",
  "rate_limit_per_minute": 50
}'
const res = await fetch('https://api.chatkomodo.com/v1/api-keys', {
  method: 'POST',
  headers: {
    Authorization: `Bearer ${process.env.CHATKOMODO_API_KEY}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    "name": "Shop integration",
    "rate_limit_per_minute": 50
  }),
});
const data = await res.json();
if (!res.ok) throw new Error(`${data.code}: ${data.error}`);
console.log(data);
import os
import requests

res = requests.post(
    "https://api.chatkomodo.com/v1/api-keys",
    headers={
        "Authorization": f"Bearer {os.environ['CHATKOMODO_API_KEY']}",
    },
    json={
        "name": "Shop integration",
        "rate_limit_per_minute": 50,
    },
    timeout=30,
)
res.raise_for_status()
print(res.json())
<?php
require 'vendor/autoload.php';

$client = new GuzzleHttp\Client();
$res = $client->request('POST', 'https://api.chatkomodo.com/v1/api-keys', [
    'headers' => [
        'Authorization' => 'Bearer ' . getenv('CHATKOMODO_API_KEY'),
    ],
    'json' => [
        'name' => 'Shop integration',
        'rate_limit_per_minute' => 50,
    ],
]);

print_r(json_decode((string) $res->getBody(), true));
Response 201
{
  "id": "01JA5V6Q8M2H7G4K9P3T0R1S2X",
  "key": "uploads/abc123.jpg",
  "prefix": "string",
  "name": "Shop integration"
}

Rename a key or change its rate limit

PATCH/v1/api-keys/{id}

Updates the name and/or the per-minute rate limit of the tenant's primary key or one of its child keys. The rate limit is the number of requests per minute the key may make with its API key; it is capped to the plan ceiling (5 times the plan's send rate per minute, at least 300), the response returns the value that was applied, and a change takes effect on the next request. Needs a dashboard login.

Path parameters

  • idstringrequired
    API key id

Request body

  • namestring
    Label, 1-64 characters
    1–64 characters
  • rate_limit_per_minuteinteger
    Requests per minute this key may make, across all /v1 endpoints (1-10 000, default 300). Capped to the plan ceiling: 5 times the plan's send rate per minute, at least 300 (600 on Pro with the default send rates)
    From 1 to 10 000

Responses

  • 200Updated
    Response fields
    • okbooleanrequired
    • rate_limit_per_minuteintegerrequired
      Rate limit now in force (after the plan cap)
  • 400Body is not valid JSON (INVALID_JSON) or fails validation (VALIDATION_FAILED)
  • 401Missing, invalid or expired credentials
  • 403Forbidden. KEY_MANAGEMENT_REQUIRES_LOGIN when the request was authenticated with an API key (keys can only be managed from a dashboard login). Also refused, without a code, for a customer-service agent login, and with KEY_SCOPE_FORBIDDEN for a session-restricted key
  • 404API key not found, or it belongs to another tenant (NOT_FOUND)
Request
curl -X PATCH "https://api.chatkomodo.com/v1/api-keys/ID" \
  -H "Authorization: Bearer $CHATKOMODO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "name": "Shop integration",
  "rate_limit_per_minute": 50
}'
const res = await fetch('https://api.chatkomodo.com/v1/api-keys/ID', {
  method: 'PATCH',
  headers: {
    Authorization: `Bearer ${process.env.CHATKOMODO_API_KEY}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    "name": "Shop integration",
    "rate_limit_per_minute": 50
  }),
});
const data = await res.json();
if (!res.ok) throw new Error(`${data.code}: ${data.error}`);
console.log(data);
import os
import requests

res = requests.patch(
    "https://api.chatkomodo.com/v1/api-keys/ID",
    headers={
        "Authorization": f"Bearer {os.environ['CHATKOMODO_API_KEY']}",
    },
    json={
        "name": "Shop integration",
        "rate_limit_per_minute": 50,
    },
    timeout=30,
)
res.raise_for_status()
print(res.json())
<?php
require 'vendor/autoload.php';

$client = new GuzzleHttp\Client();
$res = $client->request('PATCH', 'https://api.chatkomodo.com/v1/api-keys/ID', [
    'headers' => [
        'Authorization' => 'Bearer ' . getenv('CHATKOMODO_API_KEY'),
    ],
    'json' => [
        'name' => 'Shop integration',
        'rate_limit_per_minute' => 50,
    ],
]);

print_r(json_decode((string) $res->getBody(), true));
Response 200
{
  "ok": true,
  "rate_limit_per_minute": 50
}

Revoke a child API key (alias)

DEL/v1/api-keys/{id}

Alias of POST /v1/api-keys/{id}/revoke: the key is revoked, not removed, and stays in the list with revoked_at set. The primary key cannot be revoked. Needs a dashboard login.

Path parameters

  • idstringrequired
    API key id

Responses

  • 200Revoked (also returned when the key was already revoked)
    Response fields
    • okbooleanrequired
  • 400The tenant's own primary key cannot be revoked, because it would lock the tenant out of its dashboard (CANNOT_REVOKE_PRIMARY)
  • 401Missing, invalid or expired credentials
  • 403Forbidden. KEY_MANAGEMENT_REQUIRES_LOGIN when the request was authenticated with an API key (keys can only be managed from a dashboard login). Also refused, without a code, for a customer-service agent login, and with KEY_SCOPE_FORBIDDEN for a session-restricted key
  • 404API key not found, or it belongs to another tenant (NOT_FOUND)
Request
curl -X DELETE "https://api.chatkomodo.com/v1/api-keys/ID" \
  -H "Authorization: Bearer $CHATKOMODO_API_KEY"
const res = await fetch('https://api.chatkomodo.com/v1/api-keys/ID', {
  method: 'DELETE',
  headers: {
    Authorization: `Bearer ${process.env.CHATKOMODO_API_KEY}`,
  },
});
const data = await res.json();
if (!res.ok) throw new Error(`${data.code}: ${data.error}`);
console.log(data);
import os
import requests

res = requests.delete(
    "https://api.chatkomodo.com/v1/api-keys/ID",
    headers={
        "Authorization": f"Bearer {os.environ['CHATKOMODO_API_KEY']}",
    },
    timeout=30,
)
res.raise_for_status()
print(res.json())
<?php
require 'vendor/autoload.php';

$client = new GuzzleHttp\Client();
$res = $client->request('DELETE', 'https://api.chatkomodo.com/v1/api-keys/ID', [
    'headers' => [
        'Authorization' => 'Bearer ' . getenv('CHATKOMODO_API_KEY'),
    ],
]);

print_r(json_decode((string) $res->getBody(), true));
Response 200
{
  "ok": true
}

Revoke a child API key

POST/v1/api-keys/{id}/revoke

Revokes one of the tenant's child keys: it stops authenticating immediately. The key stays in the list with revoked_at set. The primary key cannot be revoked. Same effect as DELETE /v1/api-keys/{id}. Needs a dashboard login.

Path parameters

  • idstringrequired
    API key id

Responses

  • 200Revoked (also returned when the key was already revoked)
    Response fields
    • okbooleanrequired
  • 400The tenant's own primary key cannot be revoked, because it would lock the tenant out of its dashboard (CANNOT_REVOKE_PRIMARY)
  • 401Missing, invalid or expired credentials
  • 403Forbidden. KEY_MANAGEMENT_REQUIRES_LOGIN when the request was authenticated with an API key (keys can only be managed from a dashboard login). Also refused, without a code, for a customer-service agent login, and with KEY_SCOPE_FORBIDDEN for a session-restricted key
  • 404API key not found, or it belongs to another tenant (NOT_FOUND)
Request
curl -X POST "https://api.chatkomodo.com/v1/api-keys/ID/revoke" \
  -H "Authorization: Bearer $CHATKOMODO_API_KEY"
const res = await fetch('https://api.chatkomodo.com/v1/api-keys/ID/revoke', {
  method: 'POST',
  headers: {
    Authorization: `Bearer ${process.env.CHATKOMODO_API_KEY}`,
  },
});
const data = await res.json();
if (!res.ok) throw new Error(`${data.code}: ${data.error}`);
console.log(data);
import os
import requests

res = requests.post(
    "https://api.chatkomodo.com/v1/api-keys/ID/revoke",
    headers={
        "Authorization": f"Bearer {os.environ['CHATKOMODO_API_KEY']}",
    },
    timeout=30,
)
res.raise_for_status()
print(res.json())
<?php
require 'vendor/autoload.php';

$client = new GuzzleHttp\Client();
$res = $client->request('POST', 'https://api.chatkomodo.com/v1/api-keys/ID/revoke', [
    'headers' => [
        'Authorization' => 'Bearer ' . getenv('CHATKOMODO_API_KEY'),
    ],
]);

print_r(json_decode((string) $res->getBody(), true));
Response 200
{
  "ok": true
}