Skip to main content

tokolaku

Official Python SDK for the Tokolaku Engine API — AI bot replies, omnichannel messaging (WhatsApp/Instagram/Messenger), and webhook verification.

Install

pip install tokolaku

Requirements

  • Python >= 3.10

Quickstart

Bahasa Indonesia ringkas: buat instance Tokolaku dengan API key, lalu panggil bot_reply untuk balasan AI atau messages.send untuk kirim pesan lewat channel resmi (WhatsApp/Instagram/Messenger) yang sudah terhubung.

import os
from tokolaku import Tokolaku

tokolaku = Tokolaku(os.environ["TOKOLAKU_API_KEY"])
# or with options: Tokolaku(api_key=..., base_url=..., timeout=30.0, max_retries=2)

# 1. AI bot reply for a single customer message
reply = tokolaku.bot_reply(
    "Halo, apakah produk ini ready stock?",
    session_id="wa:628123456789",  # keeps multi-turn context
)
print(reply.reply, reply.parts)

# 2. Send a text message through a connected channel
sent = tokolaku.messages.send(
    to="628123456789",
    text="Terima kasih sudah menghubungi kami!",
    channel_id="ch_abc123",
)
print(sent.id, sent.status)

messages.send also accepts a business-initiated template message — pass template instead of text (exactly one of the two, never both):

tokolaku.messages.send(
    to="628123456789",
    template={"name": "order_update", "language": "id", "category": "utility"},
    channel_id="ch_abc123",
)

Error handling

Every failed request raises an instance of TokolakuAPIError (or one of its subclasses). .status is None when the request never got an HTTP response (network error, timeout); .code is the backend's machine-readable error code when available.

Class HTTP status When it's raised
TokolakuValidationError 400, 422 Invalid request params — also raised client-side before any network call (e.g. messages.send with both text and template, or neither)
TokolakuAuthenticationError 401 Missing or invalid API key
TokolakuInsufficientBalanceError 402 Tenant balance too low to cover the charge
TokolakuPermissionError 403 API key lacks permission for this action
TokolakuRateLimitError 429 Rate limit exceeded
TokolakuAPIError any other status, or None Base class — also covers network errors, timeouts, and malformed responses not mapped above
TokolakuWebhookSignatureError Webhook signature missing or invalid (does not extend TokolakuAPIError)
import os
from tokolaku import (
    Tokolaku,
    TokolakuAPIError,
    TokolakuInsufficientBalanceError,
    TokolakuRateLimitError,
)

tokolaku = Tokolaku(os.environ["TOKOLAKU_API_KEY"])

try:
    tokolaku.bot_reply("Halo")
except TokolakuInsufficientBalanceError:
    ...  # top up balance, notify the tenant
except TokolakuRateLimitError:
    ...  # back off and retry later
except TokolakuAPIError as err:
    print(err.status, err.code, err)

Retry policy

The SDK retries automatically (max_retries, default 2) using exponential backoff with full jitter (base 250ms, capped at 1s; a Retry-After response header wins when present). The policy is money-aware: it only retries when a retry cannot cause a duplicate side effect.

Condition bot_reply messages.send
429 Too Many Requests Retried Retried
Network error (request never got a response) Retried Retried
5xx server error Retried Not retried
Timeout (code: "timeout") Not retried Not retried
2xx with malformed JSON body (code: "invalid_response") Not retried Not retried
2xx where the body stream fails mid-read (code: "response_read_error") Not retried Not retried
  • bot_reply has no side effect if it fails, so it retries on 429, any 5xx, and network errors.
  • messages.send is NOT retried on timeout/5xx because the message may already have been sent and charged even though the client never saw a successful response, and the API does not yet expose an idempotency key. It only retries on 429 and network errors — a network retry only applies when the request itself failed before any response headers arrived (no response headers were ever received, so the send most likely never reached the server). Once response headers have arrived, a failure reading the body is a response_read_error, not a network error, and is never retried.
  • A timeout (code: "timeout") is never retried on either endpoint, since it's ambiguous whether the server received/processed the request.
  • A 2xx response with a body that fails to parse as JSON (code: "invalid_response") carries the actual 2xx status the server returned (usually 200) and is never retried on either endpoint — the request already reached the server and had its side effect (reply generated / message sent and charged); retrying would risk a double-send or burning AI quota for nothing.
  • A 2xx response whose body stream errors mid-read (code: "response_read_error", e.g. the connection resets after headers arrive) is likewise never retried, for the same reason: response headers arriving means the request already reached the server and may have had its side effect, even though the body was never fully read.

Webhooks

Verify the x-tokolaku-signature header (sha256=<hex>, HMAC-SHA256 of the raw request body) before trusting a webhook payload. Always use the raw, unmodified request body — a re-serialized JSON string will not match the signature.

from tokolaku.webhooks import verify_webhook_signature, construct_event, TokolakuWebhookSignatureError

Flask

import os
from flask import Flask, request, jsonify
from tokolaku.webhooks import construct_event, TokolakuWebhookSignatureError

app = Flask(__name__)


@app.post("/webhooks/tokolaku")
def tokolaku_webhook():
    raw_body = request.get_data()  # raw bytes — do NOT use request.get_json() here
    try:
        result = construct_event(
            raw_body,
            request.headers.get("x-tokolaku-signature"),
            os.environ["TOKOLAKU_WEBHOOK_SECRET"],
        )
    except TokolakuWebhookSignatureError:
        return jsonify({"error": "invalid signature"}), 401

    event = result["event"]
    # ... handle event
    return jsonify({"received": True})

FastAPI

import os
from fastapi import FastAPI, Request, HTTPException
from tokolaku.webhooks import construct_event, TokolakuWebhookSignatureError

app = FastAPI()


@app.post("/webhooks/tokolaku")
async def tokolaku_webhook(request: Request):
    raw_body = await request.body()  # raw bytes — do NOT depend on request.json() here
    try:
        result = construct_event(
            raw_body,
            request.headers.get("x-tokolaku-signature"),
            os.environ["TOKOLAKU_WEBHOOK_SECRET"],
        )
    except TokolakuWebhookSignatureError:
        raise HTTPException(status_code=401, detail="invalid signature")

    event = result["event"]
    # ... handle event
    return {"received": True}

License

MIT

Docs

Full API reference: https://tokolaku.id/api-docs

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

tokolaku-1.0.0.tar.gz (14.1 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

tokolaku-1.0.0-py3-none-any.whl (11.4 kB view details)

Uploaded Python 3

File details

Details for the file tokolaku-1.0.0.tar.gz.

File metadata

  • Download URL: tokolaku-1.0.0.tar.gz
  • Upload date:
  • Size: 14.1 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for tokolaku-1.0.0.tar.gz
Algorithm Hash digest
SHA256 6a14c6eb056688e5bc0b87a5827e2334e0f08079a4fa4f726ca049d56a20fe34
MD5 0988dd5fc1321f2b63de10a23decbc2a
BLAKE2b-256 e54b8b90e444dfe5fd792c86e5e44b4f5d28f8ef677a6a6ecc0685b46bd0d58f

See more details on using hashes here.

File details

Details for the file tokolaku-1.0.0-py3-none-any.whl.

File metadata

  • Download URL: tokolaku-1.0.0-py3-none-any.whl
  • Upload date:
  • Size: 11.4 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for tokolaku-1.0.0-py3-none-any.whl
Algorithm Hash digest
SHA256 7badd40743ce71ce0a3ef656f4f93d286360ca1403cdbae0391c154540578318
MD5 c0926287f51628d349985ef5de1914ec
BLAKE2b-256 10433902c4728a91ed56ee016c6fc34c6ea5994e85c4087e7bb1e335cfc3f959

See more details on using hashes here.

Release history Release notifications | RSS feed

1.1.0

2 files

This release

1.0.0 This release

2 files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page