PHP / Laravel SDK
causet/laravel-sdk is a Laravel package for submitting intents, running named queries, and streaming live events (intent SSE and causet-realtime ledger/projection SSE) from controllers, jobs, and services.
Webhook handlers and most controllers: use submitIntent(), not submitIntentStream(). Intent-progress SSE is a long-lived POST response — unsuitable for webhooks or short request paths. Use connectStream() to consume live Realtime events from a queue worker or artisan command, not a web request handler.
The PHP SDK is Early access. Source: github.com/Causet-Inc/causet-sdks (packages/laravel); Packagist publish in progress. See What runs today? for maturity and production-support details.
Requirements
- PHP 8.2+
- Laravel 11+ or 12+
- Guzzle 7.8+ (included with Laravel)
Installation
Packagist is not published yet. Use a path repository from a local clone:
git clone https://github.com/Causet-Inc/causet-sdks.git{
"repositories": [{ "type": "path", "url": "../causet-sdks/packages/laravel", "options": { "symlink": true } }],
"require": { "causet/laravel-sdk": "*" }
}Then:
composer update causet/laravel-sdk
php artisan vendor:publish --tag=causet-configWhen Packagist publishing completes, composer require causet/laravel-sdk will work directly.
Path repository for team contributors with source access:
{
"repositories": [{ "type": "path", "url": "../causet-sdks/packages/laravel", "options": { "symlink": true } }],
"require": { "causet/laravel-sdk": "*" }
}Configuration
CAUSET_API_URL=https://api.causet.cloud
CAUSET_PLATFORM=my-platform
CAUSET_APPLICATION=my-app
CAUSET_FORK=main
CAUSET_API_KEY=ck_live_xxx.secret
CAUSET_BEARER_TOKEN= # alternative to API keyQuick start — Facade
use Causet\Laravel\Facades\Causet;
// In a controller, job, or command
Causet::init();
$result = Causet::submitIntent('ticket_stream', 'tkt_1', 'CREATE_TICKET', [
'customer_id' => 'cust_1',
'subject' => 'Billing question',
'body' => 'I was charged twice.',
]);
if ($result['accepted']) {
$rows = Causet::runQuery('open_tickets', ['status' => 'open'], limit: 20);
}Quick start — dependency injection
use Causet\Laravel\CausetClient;
class TicketController extends Controller
{
public function __construct(private CausetClient $causet) {}
public function index()
{
$this->causet->init();
return response()->json(
$this->causet->runQuery('open_tickets', [], limit: 50)
);
}
public function close(string $ticketId)
{
$this->causet->init();
return response()->json(
$this->causet->submitIntent('ticket_stream', $ticketId, 'CLOSE_TICKET', [])
);
}
}API reference
Entity state
$client->subscribe('stream_id', 'entity_id');
$state = $client->getState('stream_id', 'entity_id'); // array|null
$client->fetchState('stream_id', 'entity_id');
$client->listEntities(streamName: 'orders', limit: 50);Intents
Use submitIntent() for webhook handlers, controllers, and jobs — synchronous HTTP submit to the runtime.
$result = $client->submitIntent(
'stream_id', 'entity_id', 'INTENT_TYPE',
['key' => 'value'],
intentId: 'optional-idempotency-key',
);
// ['accepted' => bool, 'execution_id' => ?, 'error' => ?, 'state_patch' => ?]submitIntentStream() is intent-progress SSE (not the Realtime service). It blocks until execution completes — use only in long-running CLI/queue workers when you need START / COMPLETE events, not in webhooks or typical HTTP responses.
$client->submitIntentStream(
'stream_id', 'entity_id', 'INTENT_TYPE', $payload,
function (array $ev): void {
logger()->info($ev['event'] ?? 'message', $ev['data'] ?? []);
},
);Queries
$client->runQuery('query_slug', ['param' => 'value'], limit: 30, includeTotal: true);
$client->runQuery('query_slug', null, cursor: 'abc123');
$client->listQueries();
$client->listProjections();Real-time (SSE)
Live ledger and projection events come from causet-realtime, not the SaaS API host. connectStream() blocks the calling process for the connection’s lifetime — run it from a queue job or artisan command:
Causet::on('stream_event', function (array $ev): void {
logger()->info($ev['event_type'] ?? 'event', $ev);
});
// Stream + fork + entity, resume from cursor 0
Causet::connectStream('sku_stream:sku-1', function (array $ev): void {
logger()->info($ev['event_type'] ?? 'event', $ev);
}, forkId: 'sandbox', fromCursor: 0);Stop the loop from a signal handler in long-running commands:
pcntl_async_signals(true);
pcntl_signal(SIGTERM, fn () => Causet::disconnectStream());
Causet::connectStream('sku_stream', $onEvent);Example event (delivered to your handler):
{
"cursor": 42,
"stream_id": "sku_stream",
"entity_id": "sku-1",
"fork_id": "sandbox",
"event_type": "STOCK_ADJUSTED",
"patch": [{"op": "replace", "path": "/quantity", "value": 95}]
}There is no wrapped WebSocket client (Laravel/Guzzle is synchronous) — see SSE / WebSocket for the WebSocket protocol reference if you need duplex subscriptions from a long-running worker with a separate WebSocket library.
Selectors and events
$unsub = $client->select(
'stream_id', 'entity_id',
fn (array $state) => $state['total'] ?? 0,
fn ($total) => logger()->info("Total: $total"),
);
$off = $client->on('state', function ($ev) { /* ... */ });
$off();All of the above methods are also available statically via the Causet facade (Causet::submitIntent(...), Causet::runQuery(...), etc.).
Error handling
use Causet\Laravel\Exceptions\CausetApiException;
use Causet\Laravel\Exceptions\CausetAuthException;
try {
Causet::submitIntent('ticket_stream', 'tkt_1', 'CREATE_TICKET', []);
} catch (CausetApiException $e) {
report($e);
return response()->json(['error' => $e->getMessage()], $e->statusCode);
} catch (CausetAuthException $e) {
return response()->json(['error' => 'Causet auth failed'], 401);
}Testing your app
Mock the client in Laravel tests:
use Causet\Laravel\CausetClient;
$this->mock(CausetClient::class, function ($mock) {
$mock->shouldReceive('init');
$mock->shouldReceive('runQuery')->andReturn(['items' => []]);
});HTTP endpoints
| Operation | Path |
|---|---|
| Token | POST /v1/token |
| Intent | POST /v1/runtime/platforms/{p}/applications/{a}/intents/submit |
| Intent SSE | POST /v1/runtime/stream/platforms/{p}/applications/{a}/intents/submit |
| Entity state | GET /v1/platforms/{p}/applications/{a}/entities/{stream}/{id}/state |
| Query | POST /v1/platforms/{p}/applications/{a}/forks/{fork}/queries/{slug}/run |
| Stream SSE | GET {realtimeUrl}/v1/platforms/{p}/applications/{a}/streams/{streamId}/events |
Next steps
- TypeScript / JavaScript — pair with a PHP app for real-time patches
- Testing — integration-testing patterns
- Roadmap — publishing status