ChatKomodo Docs Home Sign in

API reference · Account

Agency (resellers)

For resellers: create and manage client accounts, give them plans at your wholesale price from prepaid credit, top up, and show your own brand and domain to your clients. Ask us to make your account an agency first.

Get your agency

GET/v1/agency

Your reseller account: discount, prepaid credit, brand, custom domain, wholesale price of each plan and totals over your clients. Account owner or API key; not for support agents.

Responses

  • 200The agency
    Response fields
    • tenant_idstringrequired
    • statusstringrequired
      One of: active, suspended
    • discount_pctintegerrequired
    • creditintegerrequired
      Prepaid balance, rupiah
    • currencystringrequired
      One of: IDR
    • brandobjectrequired
    • brand.namestring | nullrequired
      Shown to your clients instead of ChatKomodo; null = not white-labelled
    • brand.colorstring | nullrequired
      #rrggbb
    • brand.logo_urlstring | nullrequired
    • brand.logo_keystring | nullrequired
    • brand.support_phonestring | nullrequired
    • brand.support_emailstring | nullrequired
    • brand.agency_idstringrequired
    • domainstring | nullrequired
    • domain_statusstringrequired
      One of: none, pending, active
    • domain_targetstringrequired
      Point a CNAME of your domain here
    • plansarray of objectrequired
    • plans[].idintegerrequired
    • plans[].namestringrequired
    • plans[].list_priceintegerrequired
      Monthly list price, rupiah
    • plans[].priceintegerrequired
      Your monthly price after the discount, rupiah
    • plans[].max_sessionsintegerrequired
    • plans[].max_messages_per_monthintegerrequired
    • max_monthsintegerrequired
    • totalsobjectrequired
    • totals.clientsintegerrequired
    • totals.activeintegerrequired
      Not suspended, on a paid plan
    • totals.devicesintegerrequired
    • totals.messages_monthintegerrequired
    • topupobjectrequired
    • topup.minintegerrequired
    • topup.maxintegerrequired
  • 404NOT_AN_AGENCY: this business is not a reseller
Request
curl "https://api.chatkomodo.com/v1/agency" \
  -H "Authorization: Bearer $CHATKOMODO_API_KEY"
const res = await fetch('https://api.chatkomodo.com/v1/agency', {
  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/agency",
    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/agency', [
    'headers' => [
        'Authorization' => 'Bearer ' . getenv('CHATKOMODO_API_KEY'),
    ],
]);

print_r(json_decode((string) $res->getBody(), true));
Response 200
{
  "tenant_id": "01JA5V6Q8M2H7G4K9P3T0R1S2X",
  "status": "active",
  "discount_pct": 12,
  "credit": 1,
  "currency": "IDR",
  "brand": {
    "name": "Example",
    "color": "#0a7350",
    "logo_url": "https://example.com/file.jpg",
    "logo_key": "uploads/abc123.jpg",
    "support_phone": "+447700900123",
    "support_email": "[email protected]",
    "agency_id": "01JA5V6Q8M2H7G4K9P3T0R1S2X"
  },
  "domain": "string",
  "domain_status": "none",
  "domain_target": "+447700900123",
  "plans": [
    {
      "id": 1,
      "name": "Example",
      "list_price": 150000,
      "price": 150000,
      "max_sessions": 1,
      "max_messages_per_month": 1
    }
  ],
  "max_months": 1,
  "totals": {
    "clients": 1,
    "active": 1,
    "devices": 1,
    "messages_month": 1
  },
  "topup": {
    "min": 1,
    "max": 1
  }
}

Set your brand

PUT/v1/agency/brand

What your clients see instead of ChatKomodo: name (in the dashboard, sign-in codes and alerts), colour, logo (upload it first with POST /v1/media; PNG, JPEG, WebP or GIF) and a support contact. Send only the fields to change; null clears one. Recorded as agency.brand.

Request body

  • brand_namestring | null
    Up to 40 characters
  • brand_colorstring | null
  • logo_keystring | null
  • support_phonestring | null
  • support_emailstring | null

Responses

  • 200The agency
    Response fields
    • tenant_idstringrequired
    • statusstringrequired
      One of: active, suspended
    • discount_pctintegerrequired
    • creditintegerrequired
      Prepaid balance, rupiah
    • currencystringrequired
      One of: IDR
    • brandobjectrequired
    • brand.namestring | nullrequired
      Shown to your clients instead of ChatKomodo; null = not white-labelled
    • brand.colorstring | nullrequired
      #rrggbb
    • brand.logo_urlstring | nullrequired
    • brand.logo_keystring | nullrequired
    • brand.support_phonestring | nullrequired
    • brand.support_emailstring | nullrequired
    • brand.agency_idstringrequired
    • domainstring | nullrequired
    • domain_statusstringrequired
      One of: none, pending, active
    • domain_targetstringrequired
      Point a CNAME of your domain here
    • plansarray of objectrequired
    • plans[].idintegerrequired
    • plans[].namestringrequired
    • plans[].list_priceintegerrequired
      Monthly list price, rupiah
    • plans[].priceintegerrequired
      Your monthly price after the discount, rupiah
    • plans[].max_sessionsintegerrequired
    • plans[].max_messages_per_monthintegerrequired
    • max_monthsintegerrequired
    • totalsobjectrequired
    • totals.clientsintegerrequired
    • totals.activeintegerrequired
      Not suspended, on a paid plan
    • totals.devicesintegerrequired
    • totals.messages_monthintegerrequired
    • topupobjectrequired
    • topup.minintegerrequired
    • topup.maxintegerrequired
  • 400VALIDATION_FAILED
  • 404NOT_AN_AGENCY: this business is not a reseller
Request
curl -X PUT "https://api.chatkomodo.com/v1/agency/brand" \
  -H "Authorization: Bearer $CHATKOMODO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "brand_name": "string",
  "brand_color": "#0a7350",
  "logo_key": "uploads/abc123.jpg",
  "support_phone": "+447700900123"
}'
const res = await fetch('https://api.chatkomodo.com/v1/agency/brand', {
  method: 'PUT',
  headers: {
    Authorization: `Bearer ${process.env.CHATKOMODO_API_KEY}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    "brand_name": "string",
    "brand_color": "#0a7350",
    "logo_key": "uploads/abc123.jpg",
    "support_phone": "+447700900123"
  }),
});
const data = await res.json();
if (!res.ok) throw new Error(`${data.code}: ${data.error}`);
console.log(data);
import os
import requests

res = requests.put(
    "https://api.chatkomodo.com/v1/agency/brand",
    headers={
        "Authorization": f"Bearer {os.environ['CHATKOMODO_API_KEY']}",
    },
    json={
        "brand_name": "string",
        "brand_color": "#0a7350",
        "logo_key": "uploads/abc123.jpg",
        "support_phone": "+447700900123",
    },
    timeout=30,
)
res.raise_for_status()
print(res.json())
<?php
require 'vendor/autoload.php';

$client = new GuzzleHttp\Client();
$res = $client->request('PUT', 'https://api.chatkomodo.com/v1/agency/brand', [
    'headers' => [
        'Authorization' => 'Bearer ' . getenv('CHATKOMODO_API_KEY'),
    ],
    'json' => [
        'brand_name' => 'string',
        'brand_color' => '#0a7350',
        'logo_key' => 'uploads/abc123.jpg',
        'support_phone' => '+447700900123',
    ],
]);

print_r(json_decode((string) $res->getBody(), true));
Response 200
{
  "tenant_id": "01JA5V6Q8M2H7G4K9P3T0R1S2X",
  "status": "active",
  "discount_pct": 12,
  "credit": 1,
  "currency": "IDR",
  "brand": {
    "name": "Example",
    "color": "#0a7350",
    "logo_url": "https://example.com/file.jpg",
    "logo_key": "uploads/abc123.jpg",
    "support_phone": "+447700900123",
    "support_email": "[email protected]",
    "agency_id": "01JA5V6Q8M2H7G4K9P3T0R1S2X"
  },
  "domain": "string",
  "domain_status": "none",
  "domain_target": "+447700900123",
  "plans": [
    {
      "id": 1,
      "name": "Example",
      "list_price": 150000,
      "price": 150000,
      "max_sessions": 1,
      "max_messages_per_month": 1
    }
  ],
  "max_months": 1,
  "totals": {
    "clients": 1,
    "active": 1,
    "devices": 1,
    "messages_month": 1
  },
  "topup": {
    "min": 1,
    "max": 1
  }
}

Set your custom domain

PUT/v1/agency/domain

Your own address for clients to sign in (e.g. app.youragency.com). Point a CNAME at domain_target; the domain becomes active once we have connected it (usually within a working day). null removes it. Account owner only. Recorded as agency.domain.

Request body

  • domainstring | nullrequired

Responses

  • 200The agency
    Response fields
    • tenant_idstringrequired
    • statusstringrequired
      One of: active, suspended
    • discount_pctintegerrequired
    • creditintegerrequired
      Prepaid balance, rupiah
    • currencystringrequired
      One of: IDR
    • brandobjectrequired
    • brand.namestring | nullrequired
      Shown to your clients instead of ChatKomodo; null = not white-labelled
    • brand.colorstring | nullrequired
      #rrggbb
    • brand.logo_urlstring | nullrequired
    • brand.logo_keystring | nullrequired
    • brand.support_phonestring | nullrequired
    • brand.support_emailstring | nullrequired
    • brand.agency_idstringrequired
    • domainstring | nullrequired
    • domain_statusstringrequired
      One of: none, pending, active
    • domain_targetstringrequired
      Point a CNAME of your domain here
    • plansarray of objectrequired
    • plans[].idintegerrequired
    • plans[].namestringrequired
    • plans[].list_priceintegerrequired
      Monthly list price, rupiah
    • plans[].priceintegerrequired
      Your monthly price after the discount, rupiah
    • plans[].max_sessionsintegerrequired
    • plans[].max_messages_per_monthintegerrequired
    • max_monthsintegerrequired
    • totalsobjectrequired
    • totals.clientsintegerrequired
    • totals.activeintegerrequired
      Not suspended, on a paid plan
    • totals.devicesintegerrequired
    • totals.messages_monthintegerrequired
    • topupobjectrequired
    • topup.minintegerrequired
    • topup.maxintegerrequired
  • 400VALIDATION_FAILED
  • 404NOT_AN_AGENCY: this business is not a reseller
  • 409DOMAIN_TAKEN
Request
curl -X PUT "https://api.chatkomodo.com/v1/agency/domain" \
  -H "Authorization: Bearer $CHATKOMODO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "domain": "string"
}'
const res = await fetch('https://api.chatkomodo.com/v1/agency/domain', {
  method: 'PUT',
  headers: {
    Authorization: `Bearer ${process.env.CHATKOMODO_API_KEY}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    "domain": "string"
  }),
});
const data = await res.json();
if (!res.ok) throw new Error(`${data.code}: ${data.error}`);
console.log(data);
import os
import requests

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

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

print_r(json_decode((string) $res->getBody(), true));
Response 200
{
  "tenant_id": "01JA5V6Q8M2H7G4K9P3T0R1S2X",
  "status": "active",
  "discount_pct": 12,
  "credit": 1,
  "currency": "IDR",
  "brand": {
    "name": "Example",
    "color": "#0a7350",
    "logo_url": "https://example.com/file.jpg",
    "logo_key": "uploads/abc123.jpg",
    "support_phone": "+447700900123",
    "support_email": "[email protected]",
    "agency_id": "01JA5V6Q8M2H7G4K9P3T0R1S2X"
  },
  "domain": "string",
  "domain_status": "none",
  "domain_target": "+447700900123",
  "plans": [
    {
      "id": 1,
      "name": "Example",
      "list_price": 150000,
      "price": 150000,
      "max_sessions": 1,
      "max_messages_per_month": 1
    }
  ],
  "max_months": 1,
  "totals": {
    "clients": 1,
    "active": 1,
    "devices": 1,
    "messages_month": 1
  },
  "topup": {
    "min": 1,
    "max": 1
  }
}

List your clients

GET/v1/agency/clients

Your client businesses with their plan, devices and messages this month.

Responses

  • 200Clients
    Response fields
    • clientsarray of objectrequired
    • clients[].idstringrequired
    • clients[].namestringrequired
    • clients[].phonestring | nullrequired
      The owner's WhatsApp number (signs in with a code)
    • clients[].plan_idinteger | nullrequired
    • clients[].plan_namestring | nullrequired
    • clients[].plan_active_untilinteger | nullrequired
      Unix time in milliseconds
    • clients[].suspendedbooleanrequired
    • clients[].created_atintegerrequired
      Unix time in milliseconds
    • clients[].devicesintegerrequired
    • clients[].connectedintegerrequired
    • clients[].messages_monthintegerrequired
  • 404NOT_AN_AGENCY: this business is not a reseller
Request
curl "https://api.chatkomodo.com/v1/agency/clients" \
  -H "Authorization: Bearer $CHATKOMODO_API_KEY"
const res = await fetch('https://api.chatkomodo.com/v1/agency/clients', {
  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/agency/clients",
    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/agency/clients', [
    'headers' => [
        'Authorization' => 'Bearer ' . getenv('CHATKOMODO_API_KEY'),
    ],
]);

print_r(json_decode((string) $res->getBody(), true));
Response 200
{
  "clients": [
    {
      "id": "01JA5V6Q8M2H7G4K9P3T0R1S2X",
      "name": "Example",
      "phone": "+447700900123",
      "plan_id": 1,
      "plan_name": "string",
      "plan_active_until": 1767225600000,
      "suspended": true,
      "created_at": 1767225600000,
      "devices": 1,
      "connected": 1,
      "messages_month": 1
    }
  ]
}

Create a client

POST/v1/agency/clients

A new business managed by you. Its owner signs in with a WhatsApp code on phone (or the one-time access_code). With plan_id, the plan is paid from your credit at your wholesale price for months (1-12); without, the client starts on Free. The API key and access code are shown only once. Recorded as agency.client_create.

Request body

  • namestringrequired
    Up to 100 characters
  • phonestringrequired
    International format
  • plan_idinteger | null
  • monthsinteger
    From 1 to 12

Responses

  • 201Created
    Response fields
    • client_idstringrequired
    • phonestringrequired
    • api_keystringrequired
    • access_codestringrequired
    • planobject | nullrequired
    • plan.plan_idintegerrequired
    • plan.monthsintegerrequired
    • plan.costintegerrequired
    • plan.active_untilintegerrequired
      Unix time in milliseconds
    • plan.creditintegerrequired
  • 400VALIDATION_FAILED or INVALID_NUMBER
  • 402INSUFFICIENT_CREDIT (with cost and credit)
  • 404NOT_AN_AGENCY: this business is not a reseller
  • 409PHONE_IN_USE
Request
curl -X POST "https://api.chatkomodo.com/v1/agency/clients" \
  -H "Authorization: Bearer $CHATKOMODO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "name": "Example",
  "phone": "+447700900123",
  "plan_id": 1,
  "months": 1
}'
const res = await fetch('https://api.chatkomodo.com/v1/agency/clients', {
  method: 'POST',
  headers: {
    Authorization: `Bearer ${process.env.CHATKOMODO_API_KEY}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    "name": "Example",
    "phone": "+447700900123",
    "plan_id": 1,
    "months": 1
  }),
});
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/agency/clients",
    headers={
        "Authorization": f"Bearer {os.environ['CHATKOMODO_API_KEY']}",
    },
    json={
        "name": "Example",
        "phone": "+447700900123",
        "plan_id": 1,
        "months": 1,
    },
    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/agency/clients', [
    'headers' => [
        'Authorization' => 'Bearer ' . getenv('CHATKOMODO_API_KEY'),
    ],
    'json' => [
        'name' => 'Example',
        'phone' => '+447700900123',
        'plan_id' => 1,
        'months' => 1,
    ],
]);

print_r(json_decode((string) $res->getBody(), true));
Response 201
{
  "client_id": "01JA5V6Q8M2H7G4K9P3T0R1S2X",
  "phone": "+447700900123",
  "api_key": "uploads/abc123.jpg",
  "access_code": "482913",
  "plan": {
    "plan_id": 1,
    "months": 1,
    "cost": 1,
    "active_until": 1767225600000,
    "credit": 1
  }
}

Give a client a plan

POST/v1/agency/clients/{id}/plan

Paid from your credit at your wholesale price. The same plan adds to the current period; another plan starts now (the rest of the old period is not refunded). Recorded as agency.client_plan.

Path parameters

  • idstringrequired

Request body

  • plan_idintegerrequired
  • monthsinteger
    From 1 to 12

Responses

  • 200Done
    Response fields
    • okbooleanrequired
    • costintegerrequired
    • creditintegerrequired
    • active_untilintegerrequired
      Unix time in milliseconds
  • 400VALIDATION_FAILED
  • 402INSUFFICIENT_CREDIT
  • 404NOT_FOUND / NOT_AN_AGENCY
Request
curl -X POST "https://api.chatkomodo.com/v1/agency/clients/ID/plan" \
  -H "Authorization: Bearer $CHATKOMODO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "plan_id": 1,
  "months": 1
}'
const res = await fetch('https://api.chatkomodo.com/v1/agency/clients/ID/plan', {
  method: 'POST',
  headers: {
    Authorization: `Bearer ${process.env.CHATKOMODO_API_KEY}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    "plan_id": 1,
    "months": 1
  }),
});
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/agency/clients/ID/plan",
    headers={
        "Authorization": f"Bearer {os.environ['CHATKOMODO_API_KEY']}",
    },
    json={
        "plan_id": 1,
        "months": 1,
    },
    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/agency/clients/ID/plan', [
    'headers' => [
        'Authorization' => 'Bearer ' . getenv('CHATKOMODO_API_KEY'),
    ],
    'json' => [
        'plan_id' => 1,
        'months' => 1,
    ],
]);

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

Suspend a client

POST/v1/agency/clients/{id}/suspend

Signs the client out and disconnects its devices until reactivated. Recorded as agency.client_suspend.

Path parameters

  • idstringrequired

Responses

  • 200Done
    Response fields
    • okbooleanrequired
    • suspendedbooleanrequired
  • 404NOT_FOUND
Request
curl -X POST "https://api.chatkomodo.com/v1/agency/clients/ID/suspend" \
  -H "Authorization: Bearer $CHATKOMODO_API_KEY"
const res = await fetch('https://api.chatkomodo.com/v1/agency/clients/ID/suspend', {
  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/agency/clients/ID/suspend",
    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/agency/clients/ID/suspend', [
    'headers' => [
        'Authorization' => 'Bearer ' . getenv('CHATKOMODO_API_KEY'),
    ],
]);

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

Reactivate a client

POST/v1/agency/clients/{id}/activate

Lets the client sign in again and reconnects its devices. Recorded as agency.client_activate.

Path parameters

  • idstringrequired

Responses

  • 200Done
    Response fields
    • okbooleanrequired
    • suspendedbooleanrequired
  • 404NOT_FOUND
Request
curl -X POST "https://api.chatkomodo.com/v1/agency/clients/ID/activate" \
  -H "Authorization: Bearer $CHATKOMODO_API_KEY"
const res = await fetch('https://api.chatkomodo.com/v1/agency/clients/ID/activate', {
  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/agency/clients/ID/activate",
    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/agency/clients/ID/activate', [
    'headers' => [
        'Authorization' => 'Bearer ' . getenv('CHATKOMODO_API_KEY'),
    ],
]);

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

Delete a client

DEL/v1/agency/clients/{id}

Deletes the client business and everything it holds, for good. confirm must repeat its name. Account owner only. Recorded as agency.client_delete.

Path parameters

  • idstringrequired

Request body

  • confirmstringrequired

Responses

  • 200Deleted
    Response fields
    • okbooleanrequired
    • sessionsintegerrequired
  • 400CONFIRMATION_REQUIRED
  • 404NOT_FOUND
Request
curl -X DELETE "https://api.chatkomodo.com/v1/agency/clients/ID" \
  -H "Authorization: Bearer $CHATKOMODO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "confirm": "string"
}'
const res = await fetch('https://api.chatkomodo.com/v1/agency/clients/ID', {
  method: 'DELETE',
  headers: {
    Authorization: `Bearer ${process.env.CHATKOMODO_API_KEY}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    "confirm": "string"
  }),
});
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/agency/clients/ID",
    headers={
        "Authorization": f"Bearer {os.environ['CHATKOMODO_API_KEY']}",
    },
    json={
        "confirm": "string",
    },
    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/agency/clients/ID', [
    'headers' => [
        'Authorization' => 'Bearer ' . getenv('CHATKOMODO_API_KEY'),
    ],
    'json' => [
        'confirm' => 'string',
    ],
]);

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

Open a client's dashboard

POST/v1/agency/clients/{id}/sign-in

Tokens to act as the client's owner (to set up devices, automations…). The session is listed in the client's signed-in devices as "Agency" and recorded in both audit logs (agency.client_sign_in, auth.agency_sign_in). Agency owner only (not an API key).

Path parameters

  • idstringrequired

Responses

  • 200Tokens
    Response fields
    • tokenstringrequired
    • refresh_tokenstringrequired
    • rolestringrequired
      One of: admin
  • 403FORBIDDEN
  • 409The client is suspended
Request
curl -X POST "https://api.chatkomodo.com/v1/agency/clients/ID/sign-in" \
  -H "Authorization: Bearer $CHATKOMODO_API_KEY"
const res = await fetch('https://api.chatkomodo.com/v1/agency/clients/ID/sign-in', {
  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/agency/clients/ID/sign-in",
    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/agency/clients/ID/sign-in', [
    'headers' => [
        'Authorization' => 'Bearer ' . getenv('CHATKOMODO_API_KEY'),
    ],
]);

print_r(json_decode((string) $res->getBody(), true));
Response 200
{
  "token": "eyJhbGciOiJIUzI1NiIs…",
  "refresh_token": "eyJhbGciOiJIUzI1NiIs…",
  "role": "admin"
}

Credit history

GET/v1/agency/ledger

Every top-up, plan purchase and adjustment, newest first, with the balance after each. Page with before = next_before.

Query parameters

  • beforestring
  • limitstring

Responses

  • 200Entries
    Response fields
    • entriesarray of objectrequired
    • entries[].idintegerrequired
    • entries[].atintegerrequired
      Unix time in milliseconds
    • entries[].kindstringrequired
      One of: topup, plan, refund, adjust
    • entries[].amountintegerrequired
      Rupiah; negative = spent
    • entries[].balanceintegerrequired
      Balance after this entry
    • entries[].client_idstring | nullrequired
    • entries[].client_namestring | nullrequired
    • entries[].plan_idinteger | nullrequired
    • entries[].plan_namestring | nullrequired
    • entries[].monthsinteger | nullrequired
    • entries[].order_idstring | nullrequired
    • entries[].notestring | nullrequired
    • next_beforeinteger | nullrequired
  • 404NOT_AN_AGENCY: this business is not a reseller
Request
curl "https://api.chatkomodo.com/v1/agency/ledger" \
  -H "Authorization: Bearer $CHATKOMODO_API_KEY"
const res = await fetch('https://api.chatkomodo.com/v1/agency/ledger', {
  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/agency/ledger",
    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/agency/ledger', [
    'headers' => [
        'Authorization' => 'Bearer ' . getenv('CHATKOMODO_API_KEY'),
    ],
]);

print_r(json_decode((string) $res->getBody(), true));
Response 200
{
  "entries": [
    {
      "id": 1,
      "at": 1767225600000,
      "kind": "topup",
      "amount": 150000,
      "balance": 1,
      "client_id": "01JA5V6Q8M2H7G4K9P3T0R1S2X",
      "client_name": "string",
      "plan_id": 1,
      "plan_name": "string",
      "months": 1,
      "order_id": "01JA5V6Q8M2H7G4K9P3T0R1S2X",
      "note": "Prefers delivery after 5 pm"
    }
  ],
  "next_before": 1767225600000
}

Top up your credit

POST/v1/agency/topup

Creates a payment for amount rupiah (50 000 - 100 000 000) with a rupiah payment method; the credit is added when the payment is confirmed (webhook, or POST /v1/billing/invoices/{order_id}/confirm). Account owner only. Recorded as agency.topup.

Request body

  • amountintegerrequired
  • providerstring

Responses

  • 201Payment created
    Response fields
    • order_idstringrequired
    • amountintegerrequired
    • currencystringrequired
      One of: IDR
    • providerstringrequired
    • pay_urlstringrequired
  • 400VALIDATION_FAILED
  • 503NOT_CONFIGURED
Request
curl -X POST "https://api.chatkomodo.com/v1/agency/topup" \
  -H "Authorization: Bearer $CHATKOMODO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "amount": 150000,
  "provider": "string"
}'
const res = await fetch('https://api.chatkomodo.com/v1/agency/topup', {
  method: 'POST',
  headers: {
    Authorization: `Bearer ${process.env.CHATKOMODO_API_KEY}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    "amount": 150000,
    "provider": "string"
  }),
});
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/agency/topup",
    headers={
        "Authorization": f"Bearer {os.environ['CHATKOMODO_API_KEY']}",
    },
    json={
        "amount": 150000,
        "provider": "string",
    },
    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/agency/topup', [
    'headers' => [
        'Authorization' => 'Bearer ' . getenv('CHATKOMODO_API_KEY'),
    ],
    'json' => [
        'amount' => 150000,
        'provider' => 'string',
    ],
]);

print_r(json_decode((string) $res->getBody(), true));
Response 201
{
  "order_id": "01JA5V6Q8M2H7G4K9P3T0R1S2X",
  "amount": 150000,
  "currency": "IDR",
  "provider": "string",
  "pay_url": "https://example.com/file.jpg"
}

Brand of a sign-in domain

GET/public/brandNo authentication

Public. The brand of an agency's activated custom domain (used by the sign-in page), or null.

Query parameters

  • hoststringrequired

Responses

  • 200Brand
    Response fields
    • brandobject | nullrequired
    • brand.namestringrequired
    • brand.colorstring | nullrequired
    • brand.logo_urlstring | nullrequired
    • brand.support_phonestring | nullrequired
    • brand.support_emailstring | nullrequired
    • brand.agency_idstringrequired
Request
curl "https://api.chatkomodo.com/public/brand?host=string"
const res = await fetch('https://api.chatkomodo.com/public/brand?host=string', {
  method: 'GET',
});
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/public/brand?host=string",
    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/public/brand?host=string', [
]);

print_r(json_decode((string) $res->getBody(), true));
Response 200
{
  "brand": {
    "name": "Example",
    "color": "#0a7350",
    "logo_url": "https://example.com/file.jpg",
    "support_phone": "+447700900123",
    "support_email": "[email protected]",
    "agency_id": "01JA5V6Q8M2H7G4K9P3T0R1S2X"
  }
}