shannonllc / avacast
PHP SDK for avacast realtime avatars: create sessions, make the avatar speak and read usage from your server.
Requires
- php: >=8.1
- ext-curl: *
- ext-json: *
Requires (Dev)
- phpunit/phpunit: ^10.5 || ^11.0 || ^12.0
Suggests
None
Provides
None
Conflicts
None
Replaces
None
README
PHP SDK for avacast realtime avatars: create sessions, make the avatar speak and read usage from your server.
- PHP 8.1+ with ext-curl. No other dependencies.
- Server-side only. Show the avatar in the browser with the browser SDK (
avacast/webfrom the Node.js SDK, orhttps://avacast.jp/sdk/v1.js), using the session'sclient_tokenandwebrtcvalues.
Install
composer require shannonllc/avacast
Quick start
use Avacast\Client; $avacast = new Client('sk_live_...'); // or set AVACAST_API_KEY $avatars = $avacast->avatars->list()['data']; $session = $avacast->sessions->create([ 'avatar_id' => $avatars[0]['id'], 'voice' => ['language' => 'ja', 'voice_id' => 'female-30s-clear'], ]); // Give only these to the browser. Never ship the API key to a browser. echo json_encode(['client_token' => $session['client_token'], 'webrtc' => $session['webrtc']]);
After the browser has connected (session.ready):
$avacast->sessions->speak($sessionId, 'ご相談ありがとうございます。'); $avacast->sessions->speak($sessionId, 'ご用件をお聞かせください。'); // no need to wait: lines are queued $avacast->sessions->interrupt($sessionId); // the user started talking $avacast->sessions->end($sessionId); // billing stops when the session closes
Methods
Params and responses are arrays with the API's field names (docs).
| Method | API |
|---|---|
sessions->create(['avatar_id' => …, 'voice' => […], 'idle_timeout_seconds' => …, 'metadata' => […]]) |
POST /v1/sessions |
sessions->speak($sessionId, $text) |
POST /v1/sessions/:id/speak |
sessions->interrupt($sessionId) |
POST /v1/sessions/:id/interrupt |
sessions->get($sessionId) |
GET /v1/sessions/:id |
sessions->end($sessionId) |
DELETE /v1/sessions/:id |
avatars->list() |
GET /v1/avatars |
usage->get() |
GET /v1/usage (this month) |
usage->range(['from' => …, 'to' => …, 'group_by' => 'metadata.<key>']) |
GET /v1/usage?from=&to=&group_by= |
status() |
GET /v1/status |
Every method takes options last: ['timeout' => seconds, 'max_retries' => n], and 'idempotency_key' => … for create and speak.
Avacast\Voices::IDS (female-standard, female-30s-clear, … male-60s-deep) and Avacast\Voices::LANGUAGES (ja, en, …) are the values for voice. Without voice_id, the session uses the avatar's voice.
$avacast = new Client(getenv('AVACAST_API_KEY'), [ 'timeout' => 30, // seconds per attempt (default) 'max_retries' => 2, // retries after the first attempt (default) ]);
Usage
$u = $avacast->usage->range([ 'from' => new DateTimeImmutable('2026-10-01T00:00:00Z'), 'to' => new DateTimeImmutable('2026-11-01T00:00:00Z'), 'group_by' => 'metadata.tenant', // sessions created with 'metadata' => ['tenant' => '…'] ]); foreach ($u['data'] as $row) { echo $row['key'], ' ', $row['connected_seconds'], "\n"; }
You are billed for the time a session is open (connected_seconds).
Errors
use Avacast\Exception\ConflictException; use Avacast\Exception\RateLimitException; try { $avacast->sessions->speak($sessionId, $text); } catch (ConflictException $e) { if ($e->errorCode === 'session_not_connected') { // wait for session.ready in the browser } } catch (RateLimitException $e) { if ($e->errorCode === 'queue_full') { // wait for speak.ended, or interrupt } }
| Exception | Status | errorCode |
|---|---|---|
ValidationException |
400 | invalid_request |
AuthenticationException |
401 | invalid_api_key |
PaymentRequiredException |
402 | payment_required (monthly limit reached) |
NotFoundException |
404 | session_not_found, avatar_not_found, not_found |
ConflictException |
409 | session_ended, session_not_connected, idempotency_conflict |
RateLimitException |
429 | rate_limited, concurrency_limit, queue_full |
ApiException |
5xx and others | gpu_unavailable (no capacity right now), maintenance |
ConnectionException |
– | Could not reach the API |
TimeoutException |
– | No response within timeout (extends ConnectionException) |
All of them extend Avacast\Exception\AvacastException with errorCode, status, requestId (quote it when you contact us) and retryAfter. Branch on errorCode; messages may change.
Retries
- 429
rate_limitedis retried afterretry-after.concurrency_limitandqueue_fullare thrown at once: close a session, or wait for the queue. - 5xx, timeouts and connection errors are retried for
get,end,avatars->list,usageandstatus, and forcreateandspeak, which send anIdempotency-Keyso that a retry does not open a second session or speak twice.interruptis not retried on these. - Waits are exponential (0.5 s, 1 s, 2 s … up to 8 s, ±25%), or
retry-afterwhen the API sends it. Aretry-afterover 60 seconds is thrown at once.
Custom HTTP client
Implement Avacast\Http\Transport (one method, send(Request): Response) to route requests through Guzzle or a PSR-18 client, or to stub the API in tests, and pass it as ['transport' => $transport].
Links
- Docs: https://avacast.jp/docs
- License: MIT