uchara / uchara-php
Official PHP SDK for Uchara Chat Platform — server-side API integration and visitor widget SDK
Fund package maintenance!
Requires
- php: ^8.1
- ext-json: *
- guzzlehttp/guzzle: ^7.5
Requires (Dev)
- phpstan/phpstan: ^1.10
- phpunit/phpunit: ^10.0
Suggests
- laravel/framework: ^10.0|^11.0
Provides
None
Conflicts
None
Replaces
None
README
Official PHP SDK for the Uchara Chat Platform. It provides:
- ServerSDK — server-to-server integration with the authenticated
/v1/*REST API (members/agents, invites, channels, bots, conversations, messages, contacts, canned responses, API keys). - AgentSDK — authenticate human workspace agents with email/password JWTs and send messages attributed to the logged-in agent.
- VisitorSDK — embed the chat widget in customer applications via the public
/v1/widget/*endpoints. - Laravel integration — a service provider, manager, facade and config file with auto-discovery for Laravel 10/11. The native SDK itself has no Laravel dependency.
Requirements
- PHP 8.1 or higher
- Composer
ext-json
Installation
composer require uchara/uchara-php
For Laravel 10/11 the service provider and facade are registered automatically via package auto-discovery. Publish the config file with:
php artisan vendor:publish --tag=uchara-config
Quick Start — Server SDK
<?php require 'vendor/autoload.php'; use Uchara\SDK\ServerSDK; $client = new ServerSDK( apiUrl: 'https://api.uchara.com', apiKey: getenv('UCHARA_API_KEY') ); // Send a message $message = $client->sendMessage('conv_abc123', [ 'content' => 'Your order has shipped!', 'sender_type' => 'bot', ]); echo "Message sent: {$message['id']}\n"; // List conversations $conversations = $client->listConversations([ 'status' => 'open', 'limit' => 10, ]); foreach ($conversations as $conv) { echo "Conversation: {$conv['id']} - {$conv['contact_name']}\n"; }
Members (a.k.a. agents)
Workspace members are the human users of a workspace. Because many users refer to them as
"agents", ergonomic Agent aliases are provided alongside the canonical Member methods.
// Canonical member methods $members = $client->listMembers(['role' => 'agent']); $member = $client->getMember('member_123'); $created = $client->createMember(['email' => 'a@b.com', 'role' => 'agent'], 'idem-key-1'); $client->updateMember('member_123', ['name' => 'Alice']); $client->updateMemberRole('member_123', 'admin'); $client->deactivateMember('member_123'); $client->reactivateMember('member_123'); $client->deleteMember('member_123'); // Agent aliases — identical behaviour $agents = $client->listAgents(); $agent = $client->getAgent('member_123'); $client->createAgent(['email' => 'a@b.com']); $client->updateAgent('member_123', ['name' => 'Alice']); $client->deactivateAgent('member_123'); $client->reactivateAgent('member_123'); $client->deleteAgent('member_123');
Invites
$invite = $client->inviteMember(['email' => 'x@y.com', 'role' => 'agent']); $invites = $client->listInvites(); $client->revokeInvite('invite_123');
Channels, Bots & Messages
$channels = $client->listChannels(); $channel = $client->getChannel('channel_123'); // filters the list (no GET-by-id route) $client->createChannel(['name' => 'WhatsApp', 'type' => 'whatsapp']); $client->updateChannel('channel_123', ['name' => 'WA']); $client->deleteChannel('channel_123'); $bots = $client->listBots(); $client->createBot(['name' => 'Support Bot']); $client->updateBot('bot_123', ['name' => 'Support Bot v2']); $client->deleteBot('bot_123'); // Message aliases $client->sendMessageToConversation('conv_1', ['content' => 'hi']); $messages = $client->listMessages('conv_1'); $messages = $client->getConversationMessages('conv_1');
Quick Start — Agent SDK
Use AgentSDK when messages must appear as a specific human agent. The API derives
sender_type=agent and sender_id from the access token, so do not provide those
fields yourself.
<?php require 'vendor/autoload.php'; use Uchara\SDK\AgentSDK; $agent = new AgentSDK('https://api.uchara.com'); $agent->login( email: getenv('UCHARA_AGENT_EMAIL'), password: getenv('UCHARA_AGENT_PASSWORD'), workspaceSlug: getenv('UCHARA_WORKSPACE_SLUG') ?: null, ); $message = $agent->sendMessage('conv_abc123', [ 'content' => 'Halo, saya siap membantu.', ]);
The access and refresh tokens are stored in the SDK instance. Refresh the session when needed:
$agent->refresh();
Multi-agent collaboration and approval flow
Conversations support multiple collaborating agents. There is no separate "approval" endpoint: an authorized assignee/admin invites an existing same-workspace member, and that member accepts by joining. Identity and authorization are always derived from the authenticated member's JWT — the SDK never sends an inviter field.
// Authorized assignee/admin invites an existing workspace member to collaborate. $agent->inviteToConversation('conv_abc123', 'member_456'); // The invited agent (a separate authenticated AgentSDK session) accepts by joining. // Join persists multi-agent membership and emits `conversation.joined`. $agent->joinConversation('conv_abc123'); // The joined agent can now operate as a collaborator (send messages, add notes, // resolve, etc.) on the conversation. $agent->sendMessage('conv_abc123', ['content' => 'Saya ikut menangani.']); // A collaborator removes their membership when done. $agent->leaveConversation('conv_abc123');
takeoverConversation() is a separate bot-to-agent takeover — it transitions a
bot conversation to open and assigns it to the calling agent. It is not a
collaborator approval step and does not replace the invite → join flow above.
// Bot-to-agent takeover (distinct from collaborator approval). $agent->takeoverConversation('conv_abc123');
Secure backend-to-browser agent session (legacy agent-token flow)
Prefer the one-time dashboard SSO flow described under the Server SDK section below for seamless dashboard handoff. This agent-token flow remains supported for existing integrations.
For a custom browser dashboard, do not expose the Server API key. Create the short-lived agent session on your backend and return only the token pair to the browser:
// Backend only — UCHARA_API_KEY must remain server-side. $server = new ServerSDK('https://api.uchara.com', getenv('UCHARA_API_KEY')); $session = $server->createAgentSession('agent@example.com'); // Return $session to your dashboard frontend over your own authenticated HTTPS endpoint.
Then initialize the browser Agent SDK with the returned session:
const agent = new AgentSDK({ apiURL: 'https://api.uchara.com', autoConnect: false }); agent.loginWithToken(session);
The agent-token endpoint verifies that the email belongs to an active member in
the API key's workspace. Access tokens are short-lived; keep and rotate the refresh
token according to your frontend session policy.
One-time dashboard SSO
For a seamless handoff from your own authenticated backend to the Uchara
dashboard, issue a one-time SSO ticket with the Server SDK and return the
resulting redirect_url to the browser. The API key stays on your backend; the
browser only ever sees an opaque, short-lived ticket in the URL fragment.
There is no dedicated SDK helper for this — use the underlying HTTP client via
ServerSDK::http()->post(...):
<?php require 'vendor/autoload.php'; use Uchara\SDK\ServerSDK; // Backend only — UCHARA_API_KEY must remain server-side. $server = new ServerSDK('https://api.uchara.com', getenv('UCHARA_API_KEY')); // Issue a one-time ticket for the target member (optional channel scope). $result = $server->http()->post('/v1/auth/sso/ticket', [ 'email' => 'agent@company.com', // 'channel_ids' => ['<channel-uuid>'], // optional; omitted = full workspace ]); // Return $result['redirect_url'] from your own authenticated HTTPS endpoint. // The browser opens it; the dashboard bootstrap exchanges the ticket and // strips it from the URL. $redirectUrl = $result['redirect_url'];
Security guidance:
- Keep the API key backend-only — never expose it in browser code.
- Do not log, store, or reuse
redirect_urlvalues. - The ticket is short-lived (60 seconds), one-time, and consumed atomically on exchange; only its hash is stored server-side.
- No JWT, API key, or refresh token ever appears in the URL.
Quick Start — Visitor SDK
<?php require 'vendor/autoload.php'; use Uchara\SDK\VisitorSDK; $visitor = new VisitorSDK( apiUrl: 'https://api.uchara.com', widgetToken: 'widget_token_123' ); // Create a visitor session (stores the visitor JWT for subsequent calls) $session = $visitor->init(['name' => 'Alice', 'email' => 'a@b.com']); $config = $visitor->getConfig(); $active = $visitor->getActiveConversation(); // null when none exists if ($active === null) { $active = $visitor->startConversation(['message' => 'Hello']); } $visitor->sendMessage($active['id'], ['content' => 'Hi there']); $messages = $visitor->getMessages($active['id'], ['limit' => 20]); $visitor->close($active['id']);
Read receipts & delivery status
Every message carries a delivery_status field (plus delivery_status_at and,
on failure, delivery_error). The lifecycle is monotonic and idempotent:
queued → sent → delivered → read
with failed (and recovered for messages that later succeed). The possible
values are exposed as constants on Uchara\SDK\DeliveryStatus:
use Uchara\SDK\DeliveryStatus; if ($message['delivery_status'] === DeliveryStatus::READ) { // the recipient has read the message }
Mark a conversation as read so the other side sees a read receipt on their own sent messages:
// Visitor marks the responder's messages as read (widget) $visitor->markConversationRead($conversationId); // ['updated' => int] // Agent marks the visitor's messages as read (dashboard) $agent->markConversationRead($conversationId); // ['marked_read' => int]
Factory
The Uchara factory builds SDK instances from a config array or directly:
use Uchara\SDK\Uchara; $server = Uchara::server('https://api.uchara.com', 'uchara_sk_...'); $agent = Uchara::agent('https://api.uchara.com'); $visitor = Uchara::visitor('https://api.uchara.com', 'widget_token_...'); // From a config array $sdk = Uchara::make([ 'api_url' => 'https://api.uchara.com', 'api_key' => 'uchara_sk_...', 'access_token' => 'agent_access_token', 'default' => 'server', // or 'agent' or 'visitor' ]);
Laravel
Set the environment variables and use the facade:
// .env UCHARA_API_URL=https://api.uchara.com UCHARA_API_KEY=uchara_sk_... UCHARA_ACCESS_TOKEN=agent_access_token UCHARA_DEFAULT=server
use Uchara\SDK\Laravel\Facades\Uchara; $members = Uchara::listMembers(); // forwards to the default SDK $server = Uchara::server(); // explicit ServerSDK $agent = Uchara::agent(); // explicit AgentSDK $visitor = Uchara::visitor(); // explicit VisitorSDK
Error Handling
use Uchara\SDK\UcharaException; try { $message = $client->sendMessage('conv_id', ['content' => 'Hello!']); } catch (UcharaException $e) { echo "Error ({$e->getStatus()}): {$e->getMessage()}\n"; if ($e->getDetails()) { print_r($e->getDetails()); } }
UcharaException exposes the HTTP status via getStatus() (alias of getCode()), the parsed
error payload via getDetails(), and the full structured response via getResponse().
Advanced HTTP access
The simple helpers (get/post/patch/put/delete) return the unwrapped data payload.
When you need the status code, pagination meta or response headers, use request():
$response = $client->http()->request('GET', '/v1/workspace/members', ['query' => ['limit' => 10]]); $status = $response->status(); $meta = $response->meta(); $data = $response->data();
Development
composer install composer test # PHPUnit composer analyse # PHPStan composer validate # composer validate --strict
Documentation
Full documentation: https://www.uchara.com/docs/sdk/php
License
MIT