Skip to content

PHP SDK

The official PHP SDK (uchara/uchara-php) for backend integration with the Uchara Chat Platform. It provides a Server SDK for server-to-server calls (authenticated with an API key), an Agent SDK for human-agent JWT sessions, and a Visitor SDK for embedding the chat widget in customer applications.

  • PHP ^8.1
  • Composer
  • ext-json
  • Guzzle ^7.5 (installed automatically)
Terminal window
composer require uchara/uchara-php

The Uchara factory class provides ergonomic constructors for all three SDKs, plus a make() helper that builds the appropriate SDK from a configuration array.

<?php
require 'vendor/autoload.php';
use Uchara\SDK\Uchara;
// Server SDK (API key)
$server = Uchara::server(
apiUrl: 'https://api.uchara.com',
apiKey: getenv('UCHARA_API_KEY'),
);
// Agent SDK (human-agent JWT)
$agent = Uchara::agent(
apiUrl: 'https://api.uchara.com',
);
$agent->login(
email: getenv('UCHARA_AGENT_EMAIL'),
password: getenv('UCHARA_AGENT_PASSWORD'),
);
// Visitor SDK (widget token)
$visitor = Uchara::visitor(
apiUrl: 'https://api.uchara.com',
widgetToken: 'wgt_your_token',
);
// Or build from a config array
$sdk = Uchara::make([
'api_url' => 'https://api.uchara.com',
'api_key' => getenv('UCHARA_API_KEY'),
'access_token' => getenv('UCHARA_ACCESS_TOKEN'),
'default' => 'server', // or 'agent' or 'visitor'
'widget_token' => 'wgt_your_token',
'timeout' => 30,
]);

The package ships a service provider, a facade, and a config file.

Terminal window
php artisan vendor:publish --tag=uchara-config
UCHARA_API_URL=https://api.uchara.com
UCHARA_API_KEY=uchara_sk_...
UCHARA_ACCESS_TOKEN=agent_access_token
UCHARA_WIDGET_TOKEN=wgt_your_token
UCHARA_DEFAULT=server
UCHARA_TIMEOUT=30
use Uchara\SDK\Laravel\Facades\Uchara;
// The facade forwards to the default SDK, so you can call any SDK method:
$members = Uchara::listMembers();
// Or resolve a specific SDK:
$server = Uchara::server(); // ServerSDK
$agent = Uchara::agent(); // AgentSDK
$visitor = Uchara::visitor(); // VisitorSDK
$sdk = Uchara::sdk(); // default SDK

The manager is registered as the uchara singleton and caches SDK instances, so Uchara::listMembers() works out of the box.

Use the Agent SDK when a message must be shown as the logged-in human agent rather than as a bot. Login stores the access and refresh tokens in the SDK instance. The API derives the sender identity from the JWT; sender_type and sender_id should not be included in message payloads.

use Uchara\SDK\AgentSDK;
$agent = new AgentSDK('https://api.uchara.com');
$login = $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.',
]);
// Exchange the stored refresh token when the access token expires.
$agent->refresh();

Secure backend-to-browser session (legacy agent-token flow)

Section titled “Secure backend-to-browser session (legacy agent-token flow)”

Prefer the one-time dashboard SSO flow described under the Server SDK below for seamless dashboard handoff. This agent-token flow remains supported for existing integrations.

Never expose a Server API key in browser code. Create a short-lived agent session on your backend, then return only that session to your authenticated dashboard:

// Backend only.
$server = Uchara::server(
apiUrl: 'https://api.uchara.com',
apiKey: getenv('UCHARA_API_KEY'),
);
$session = $server->createAgentSession('agent@example.com');
// Return $session from your own authenticated HTTPS endpoint.

In the browser, initialize the Agent SDK with the returned token, refresh_token, and member values:

const agent = new AgentSDK({ apiURL: 'https://api.uchara.com' });
agent.loginWithToken(session);

The API key’s workspace must contain an active member with the requested email. The access token is short-lived; rotate it with the refresh token.

Available agent operations include getMe, updateAvailability, conversation and message methods, assignment/workflow actions, notes, contacts, members, and channels.

The ServerSDK authenticates with a Server SDK API key (uchara_sk_...) and wraps the authenticated /v1/* REST endpoints. All methods return the unwrapped data payload as a PHP array.

<?php
use Uchara\SDK\Uchara;
use Uchara\SDK\UcharaException;
$client = Uchara::server(
apiUrl: 'https://api.uchara.com',
apiKey: getenv('UCHARA_API_KEY'),
);
try {
// Send a message (defaults to sender_type 'bot')
$message = $client->sendMessage('conv_abc123', [
'content' => 'Your order has shipped! 🚚',
]);
// List open conversations
$conversations = $client->listConversations([
'status' => 'open',
'limit' => 10,
]);
// Upsert a contact (match by external_id)
$contact = $client->upsertContact([
'external_id' => 'user_123',
'name' => 'John Doe',
'email' => 'john@example.com',
]);
} catch (UcharaException $e) {
echo "Error ({$e->getStatus()}): {$e->getMessage()}\n";
print_r($e->getDetails());
}
$client->getMe(): array
$client->updateMe(array $data): array
$client->listMyWorkspaces(): array
$client->updateMyAvailability(string $availability): array
$client->getWorkspace(): array
$client->upsertContact(array $data): array
$client->getContact(string $contactId): array
$client->listContacts(array $options = []): array
$client->getConversation(string $conversationId): array
$client->listConversations(array $filters = []): array
$client->assignConversation(string $conversationId, ?string $agentId): array
$client->resolveConversation(string $conversationId): array
$client->updateConversation(string $conversationId, array $data): array
$client->takeoverConversation(string $conversationId): array
$client->joinConversation(string $conversationId): array
$client->leaveConversation(string $conversationId): array
$client->inviteToConversation(string $conversationId, array $data): array
$client->listConversationNotes(string $conversationId): array
$client->addConversationNote(string $conversationId, array $data): array
$client->sendMessage(string $conversationId, array $data): array
$client->getMessages(string $conversationId, array $options = []): array
// Aliases
$client->sendMessageToConversation(string $conversationId, array $data): array
$client->listMessages(string $conversationId, array $options = []): array
$client->getConversationMessages(string $conversationId, array $options = []): array
$client->listChannels(): array
$client->getChannel(string $channelId): array
$client->createChannel(array $data): array
$client->updateChannel(string $channelId, array $data): array
$client->deleteChannel(string $channelId): array
$client->setupChannelWebhook(string $channelId, array $data = []): array
$client->testChannelConnection(string $channelId, array $data = []): array

Members are the human users of a workspace. Because many users refer to them as “agents”, ergonomic aliases (listAgents / getAgent / createAgent / …) are provided alongside the canonical member methods.

$client->listMembers(array $query = []): array
$client->getMember(string $memberId): array
$client->createMember(array $payload, ?string $idempotencyKey = null): array
$client->updateMember(string $memberId, array $payload): array
$client->updateMemberRole(string $memberId, string $role): array
$client->deactivateMember(string $memberId): array
$client->reactivateMember(string $memberId): array
$client->deleteMember(string $memberId): array

Members (human agents) can be provisioned directly via the Server SDK — no email/password registration is required. This endpoint is restricted to Server SDK API keys (not ordinary JWTs). Pass an optional Idempotency-Key header so retries do not create duplicate members:

$member = $client->createMember(
['email' => 'agent@company.com', 'name' => 'Agent One', 'role' => 'agent'],
idempotencyKey: 'provision-2024-0001',
);
$client->listAgents(array $query = []): array
$client->getAgent(string $agentId): array
$client->createAgent(array $payload, ?string $idempotencyKey = null): array
$client->updateAgent(string $agentId, array $payload): array
$client->updateAgentRole(string $agentId, string $role): array
$client->deactivateAgent(string $agentId): array
$client->reactivateAgent(string $agentId): array
$client->deleteAgent(string $agentId): array
$client->inviteAgent(array $payload): array
$client->inviteMember(array $payload): array
$client->listInvites(): array
$client->revokeInvite(string $inviteId): array

Bots are a separate resource from human members/agents. Manage them with the dedicated bot methods:

$client->listBots(): array
$client->createBot(array $data): array
$client->updateBot(string $botId, array $data): array
$client->deleteBot(string $botId): array
$client->listCannedResponses(): array
$client->createCannedResponse(array $data): array
$client->updateCannedResponse(string $id, array $data): array
$client->deleteCannedResponse(string $id): array
$client->createApiKey(array $data): array
$client->listApiKeys(): array
$client->revokeApiKey(string $keyId): void

Security: The raw key value is returned only once, in the createApiKey response, and cannot be retrieved again later. Store it immediately and treat it as a secret — never commit it to git.

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
use Uchara\SDK\Uchara;
// Backend only — UCHARA_API_KEY must remain server-side.
$server = Uchara::server(
apiUrl: 'https://api.uchara.com',
apiKey: 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_url values.
  • 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.

The VisitorSDK wraps the public /v1/widget/* REST endpoints for embedding the chat widget in customer applications. Call init() first to create a visitor session and obtain a visitor JWT; subsequent authenticated calls use that token.

<?php
use Uchara\SDK\Uchara;
$visitor = Uchara::visitor(
apiUrl: 'https://api.uchara.com',
widgetToken: 'wgt_your_token',
);
// Create a visitor session (optional identity fields)
$session = $visitor->init([
'external_id' => 'user_123',
'name' => 'Jane Doe',
'email' => 'jane@example.com',
'phone' => '+6281234567890',
]);
// Public widget configuration
$config = $visitor->getConfig();
// Active conversation, or null when none exists
$active = $visitor->getActiveConversation();
// Start a conversation
$conv = $visitor->startConversation(['message' => 'Hello!']);
// Messages (limit/offset pagination)
$messages = $visitor->getMessages($conv['id'], ['limit' => 50, 'offset' => 0]);
// Send a message as the visitor
$visitor->sendMessage($conv['id'], ['content' => 'Thanks!']);
// Upload a file (multipart/form-data)
$visitor->upload($conv['id'], '/path/to/receipt.pdf');
// Download the transcript as plain text
$text = $visitor->download($conv['id']);
// Close the conversation
$visitor->close($conv['id']); // or closeConversation()
$visitor->init(array $contact = []): array
$visitor->getConfig(): array
$visitor->getActiveConversation(): ?array
$visitor->startConversation(array $payload = []): array
$visitor->getMessages(string $conversationId, array $options = []): array
$visitor->sendMessage(string $conversationId, array $payload): array
$visitor->upload(string $conversationId, string $filePath, ?string $filename = null, ?string $mimeType = null): array
$visitor->download(string $conversationId): string
$visitor->close(string $conversationId): array
$visitor->closeConversation(string $conversationId): array
$visitor->getVisitorToken(): ?string
$visitor->getContactId(): ?string

The SDK throws a structured UcharaException on transport errors and non-2xx responses. It exposes the HTTP status code through both the standard getCode() and the more explicit getStatus() alias, plus the parsed error payload and the full structured response.

use Uchara\SDK\UcharaException;
try {
$client->sendMessage('conv_abc123', ['content' => 'Hello']);
} catch (UcharaException $e) {
echo "Status: {$e->getStatus()}\n"; // HTTP status code
echo "Message: {$e->getMessage()}\n";
print_r($e->getDetails()); // parsed error payload from the API
$response = $e->getResponse(); // ?UcharaResponse for advanced inspection
}

For advanced callers, ServerSDK::http()->request(...) returns a full UcharaResponse exposing the status, unwrapped data, pagination meta, headers, and raw body:

$response = $client->http()->request('GET', '/v1/conversations', ['query' => ['limit' => 10]]);
$response->status(); // int
$response->data(); // array (unwrapped data payload)
$response->meta(); // array (pagination / metadata envelope)
$response->headers(); // array
$response->rawBody(); // ?string
$response->successful(); // bool
  • The Server SDK API key (uchara_sk_...) is a secret — load it from an environment variable, never hardcode or commit it.
  • The raw key value is returned only once when created; store it immediately.
  • Keys can be revoked instantly via revokeApiKey.
  • The Visitor SDK uses a public widget token, which is safe to embed in client-side code.