PHPackages                             callisto-php/sdk - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [API Development](/categories/api)
4. /
5. callisto-php/sdk

ActiveLibrary[API Development](/categories/api)

callisto-php/sdk
================

Official Callisto Signal API SDK for PHP

00PHPCI failing

Since Jun 1Pushed 6d agoCompare

[ Source](https://github.com/papacandco/callisto-php-sdk)[ Packagist](https://packagist.org/packages/callisto-php/sdk)[ RSS](/packages/callisto-php-sdk/feed)WikiDiscussions main Synced 1w ago

READMEChangelogDependenciesVersions (1)Used By (0)

callisto/sdk (PHP)
==================

[](#callistosdk-php)

Official Callisto messaging API SDK for PHP 8.1+.

Requirements
------------

[](#requirements)

- PHP **8.1+** (uses enums, readonly properties, named arguments)
- [`ext-json`](https://www.php.net/manual/en/book.json.php) (bundled with PHP)
- [Guzzle](https://docs.guzzlephp.org/) `^7.5` — the HTTP transport (installed automatically)

Install
-------

[](#install)

```
composer require callisto/sdk
```

Configuration
-------------

[](#configuration)

The client is constructed directly with named arguments. The constructor signature is:

```
public function __construct(
    ?string $clientId = null,
    ?string $apiKey = null,
    ?string $baseUrl = null,
    float $timeout = 30.0,
    ?GuzzleHttp\Client $httpClient = null,
)
```

```
use Callisto\Sdk\Client;

$callisto = new Client(
    clientId: 'your-client-id',
    apiKey: 'your-api-key',
    baseUrl: 'https://api.callistosignal.com/v1', // optional
    timeout: 30.0,                                 // optional, seconds
);
```

**Defaults**

ArgumentDefault`baseUrl``https://api.callistosignal.com/v1``timeout``30.0` (seconds)`httpClient`a fresh `GuzzleHttp\Client` (inject your own for testing)**Environment-variable fallback** — when `clientId`, `apiKey`, or `baseUrl` are omitted (or `null`), they are resolved from the environment:

ArgumentEnv var`clientId``CALLISTO_CLIENT_ID``apiKey``CALLISTO_API_KEY``baseUrl``CALLISTO_BASE_URL````
use Callisto\Sdk\Client;

// Reads CALLISTO_CLIENT_ID / CALLISTO_API_KEY / CALLISTO_BASE_URL.
$callisto = new Client();
```

If neither the `clientId`/`apiKey` arguments nor their env vars are set, the constructor throws `InvalidArgumentException`. A trailing slash on `baseUrl` is stripped automatically.

**Authentication** is HTTP Basic and applied automatically on every request — `clientId` is the username and `apiKey` is the password. You never set headers yourself.

Quick start
-----------

[](#quick-start)

```
use Callisto\Sdk\Client;
use Callisto\Sdk\Exception\CallistoException;

$callisto = new Client(clientId: 'your-client-id', apiKey: 'your-api-key');

try {
    // Check your balance
    $balance = $callisto->balance()->get();

    // Send an SMS
    $result = $callisto->sms()->send(
        sender: 'Acme',
        to: '+2250700000000',
        message: 'Welcome to Acme!',
    );

    echo $result['status'];
} catch (CallistoException $e) {
    echo "API error ({$e->getStatusCode()}): {$e->getMessage()}";
}
```

Resources
---------

[](#resources)

Resources are reached through accessor methods on the client: `balance()`, `sms()`, `otp()`, `whatsApp()`, and `notify()`. Each accessor returns a cached resource instance.

> **Reads vs. writes.** Write/action endpoints (`send`, `verify`, `balance`) return a raw `array` decoded straight from the JSON response. Typed **read** endpoints (`getStatus`, `getInstance`, `getMessage`, and the `list*` methods) return [typed model objects](#typed-models) instead. The per-method tables below state the exact return type.

---

### `balance()`

[](#balance)

#### `get(string $format = 'full', ?string $currency = null): array`

[](#getstring-format--full-string-currency--null-array)

ParameterTypeRequiredDescription`format``string`noResponse detail level. Defaults to `'full'`.`currency``?string`noCurrency code to express the balance in (optional).Returns `array`.

```
$balance = $callisto->balance()->get();
$balance = $callisto->balance()->get(format: 'full', currency: 'XOF');
```

---

### `sms()`

[](#sms)

#### `send(string $sender, string|array $to, string $message, ?string $notifyUrl = null, ?string $scheduledAt = null): array`

[](#sendstring-sender-stringarray-to-string-message-string-notifyurl--null-string-scheduledat--null-array)

ParameterTypeRequiredDescription`sender``string`yesApproved sender name, e.g. `"Acme"`.`to``string | array`yesOne recipient (string) or many (array of E.164 numbers).`message``string`yesMessage body.`notifyUrl``?string`noWebhook URL for delivery-status callbacks.`scheduledAt``?string`noSchedule send time, e.g. `"2026-06-02 10:00:00"`.Returns `array`.

```
$callisto->sms()->send(
    sender: 'Acme',
    to: '+2250700000000',
    message: 'Your code is 1234',
);

// Multiple recipients + scheduling
$callisto->sms()->send(
    sender: 'Acme',
    to: ['+2250700000000', '+2250700000001'],
    message: 'Sale starts tomorrow!',
    notifyUrl: 'https://acme.example/webhooks/sms',
    scheduledAt: '2026-06-02 10:00:00',
);
```

#### `list(?string $startedAt = null, ?string $endedAt = null, ?int $page = null, ?int $perPage = null): Paginated`

[](#liststring-startedat--null-string-endedat--null-int-page--null-int-perpage--null-paginated)

ParameterTypeRequiredDescription`startedAt``?string`noFilter: start of date range.`endedAt``?string`noFilter: end of date range.`page``?int`noPage number.`perPage``?int`noItems per page.Returns [`Paginated`](#paginated) of [`SmsMessage`](#smsmessage).

```
$messages = $callisto->sms()->list(page: 1, perPage: 50);
foreach ($messages->items as $msg) {
    echo "{$msg->recipient}: {$msg->status}\n";
}
```

#### `getStatus(string $messageId): SmsMessage`

[](#getstatusstring-messageid-smsmessage)

ParameterTypeRequiredDescription`messageId``string`yesThe SMS message ID.Returns [`SmsMessage`](#smsmessage).

```
$msg = $callisto->sms()->getStatus('msg_abc123');
echo $msg->status;
```

---

### `otp()`

[](#otp)

#### `send(string $to, string $message, ?string $sender = null, ?int $expiredIn = null, OtpType|string|null $type = null, ?int $digitSize = null, OtpProvider|string|null $provider = null, ?string $instanceCode = null): array`

[](#sendstring-to-string-message-string-sender--null-int-expiredin--null-otptypestringnull-type--null-int-digitsize--null-otpproviderstringnull-provider--null-string-instancecode--null-array)

ParameterTypeRequiredDescription`to``string`yesRecipient phone number.`message``string`yesMessage template; typically contains a placeholder for the code.`sender``?string`noSender name.`expiredIn``?int`noCode lifetime in seconds.`type`[`OtpType`](#otptype)`|string`noCode character set (`digit`, `alpha`, `alphanumeric`).`digitSize``?int`noNumber of characters in the code.`provider`[`OtpProvider`](#otpprovider)`|string`noDelivery channel (`sms`, `whatsapp`).`instanceCode``?string`conditionalWhatsApp instance code. **Required when `provider` is `whatsapp`** — the SDK throws `ValidationException` otherwise.Returns `array`.

```
use Callisto\Sdk\Enum\OtpType;
use Callisto\Sdk\Enum\OtpProvider;

// SMS OTP
$otp = $callisto->otp()->send(
    to: '+2250700000000',
    message: 'Your Acme code is {{code}}',
    sender: 'Acme',
    type: OtpType::Digit,
    digitSize: 6,
    expiredIn: 300,
);

// WhatsApp OTP — instanceCode is required
$callisto->otp()->send(
    to: '+2250700000000',
    message: 'Your code is {{code}}',
    provider: OtpProvider::Whatsapp,
    instanceCode: 'inst_abc123',
);
```

#### `verify(string $otpId, string $code): array`

[](#verifystring-otpid-string-code-array)

ParameterTypeRequiredDescription`otpId``string`yesThe OTP ID from `send()`.`code``string`yesThe code entered by the user.Returns `array`.

```
$result = $callisto->otp()->verify(otpId: 'otp_abc123', code: '123456');
```

#### `getStatus(string $otpId): Otp`

[](#getstatusstring-otpid-otp)

ParameterTypeRequiredDescription`otpId``string`yesThe OTP ID.Returns [`Otp`](#otp-model).

```
$otp = $callisto->otp()->getStatus('otp_abc123');
echo $otp->status;
```

#### `list(?string $startedAt = null, ?string $endedAt = null, ?int $page = null, ?int $limit = null): Paginated`

[](#liststring-startedat--null-string-endedat--null-int-page--null-int-limit--null-paginated)

ParameterTypeRequiredDescription`startedAt``?string`noFilter: start of date range.`endedAt``?string`noFilter: end of date range.`page``?int`noPage number.`limit``?int`noItems per page.Returns [`Paginated`](#paginated) of [`Otp`](#otp-model).

```
$otps = $callisto->otp()->list(page: 1, limit: 20);
foreach ($otps->items as $otp) {
    echo "{$otp->recipient}: {$otp->status}\n";
}
```

---

### `whatsApp()`

[](#whatsapp)

#### `createInstance(string $name, ?string $phoneNumber = null, ?string $webhookUrl = null, ?string $idempotencyKey = null): WhatsAppInstance`

[](#createinstancestring-name-string-phonenumber--null-string-webhookurl--null-string-idempotencykey--null-whatsappinstance)

ParameterTypeRequiredDescription`name``string`yesDisplay name for the instance.`phoneNumber``?string`noPhone number to bind to the instance.`webhookUrl``?string`noWebhook URL for inbound/status events.`idempotencyKey``?string`noKey to make instance creation idempotent.Returns [`WhatsAppInstance`](#whatsappinstance).

```
$instance = $callisto->whatsApp()->createInstance(
    name: 'Acme Support',
    phoneNumber: '+2250700000000',
    webhookUrl: 'https://acme.example/webhooks/wa',
);
echo $instance->code;
```

#### `listInstances(int $page = 1): Paginated`

[](#listinstancesint-page--1-paginated)

ParameterTypeRequiredDescription`page``int`noPage number (default `1`).Returns [`Paginated`](#paginated) of [`WhatsAppInstance`](#whatsappinstance).

```
$instances = $callisto->whatsApp()->listInstances(page: 1);
```

#### `getInstance(string $code): WhatsAppInstance`

[](#getinstancestring-code-whatsappinstance)

ParameterTypeRequiredDescription`code``string`yesThe instance code.Returns [`WhatsAppInstance`](#whatsappinstance).

```
$instance = $callisto->whatsApp()->getInstance('inst_abc123');
```

#### `getQr(string $code): array`

[](#getqrstring-code-array)

ParameterTypeRequiredDescription`code``string`yesThe instance code.Returns `array` (the QR-code payload to scan for pairing).

```
$qr = $callisto->whatsApp()->getQr('inst_abc123');
```

#### `getStatus(string $code): array`

[](#getstatusstring-code-array)

ParameterTypeRequiredDescription`code``string`yesThe instance code.Returns `array` (connection/session status of the instance).

```
$status = $callisto->whatsApp()->getStatus('inst_abc123');
```

#### `listMessages(string $code, ?string $startedAt = null, ?string $endedAt = null, ?int $page = null, ?int $perPage = null): Paginated`

[](#listmessagesstring-code-string-startedat--null-string-endedat--null-int-page--null-int-perpage--null-paginated)

ParameterTypeRequiredDescription`code``string`yesThe instance code.`startedAt``?string`noFilter: start of date range.`endedAt``?string`noFilter: end of date range.`page``?int`noPage number.`perPage``?int`noItems per page.Returns [`Paginated`](#paginated) of [`WhatsAppMessage`](#whatsappmessage).

```
$messages = $callisto->whatsApp()->listMessages('inst_abc123', page: 1, perPage: 50);
```

#### `getMessage(string $messageId): WhatsAppMessage`

[](#getmessagestring-messageid-whatsappmessage)

ParameterTypeRequiredDescription`messageId``string`yesThe WhatsApp message ID.Returns [`WhatsAppMessage`](#whatsappmessage).

```
$msg = $callisto->whatsApp()->getMessage('wamsg_abc123');
echo $msg->status;
```

#### `sendText(string $code, string $to, string $message, ?string $scheduledAt = null): array`

[](#sendtextstring-code-string-to-string-message-string-scheduledat--null-array)

ParameterTypeRequiredDescription`code``string`yesThe instance code.`to``string`yesRecipient phone number.`message``string`yesText message body.`scheduledAt``?string`noSchedule send time.Returns `array`.

```
$callisto->whatsApp()->sendText(
    code: 'inst_abc123',
    to: '+2250700000000',
    message: 'Hello from Acme!',
);
```

#### `sendMedia(string $code, string $to, WhatsAppMediaType|string $type, string $mediaUrl, ?string $caption = null, ?string $filename = null, ?string $scheduledAt = null): array`

[](#sendmediastring-code-string-to-whatsappmediatypestring-type-string-mediaurl-string-caption--null-string-filename--null-string-scheduledat--null-array)

ParameterTypeRequiredDescription`code``string`yesThe instance code.`to``string`yesRecipient phone number.`type`[`WhatsAppMediaType`](#whatsappmediatype)`|string`yes`image`, `video`, `document`, `audio`.`mediaUrl``string`yesURL of the media to send.`caption``?string`noCaption text.`filename``?string`noFilename (useful for documents).`scheduledAt``?string`noSchedule send time.Returns `array`.

```
use Callisto\Sdk\Enum\WhatsAppMediaType;

$callisto->whatsApp()->sendMedia(
    code: 'inst_abc123',
    to: '+2250700000000',
    type: WhatsAppMediaType::Image,
    mediaUrl: 'https://acme.example/promo.jpg',
    caption: 'Check this out!',
);
```

#### `sendButtons(string $code, string $to, string $body, array $buttons, ?string $header = null, ?string $footer = null, ?string $scheduledAt = null): array`

[](#sendbuttonsstring-code-string-to-string-body-array-buttons-string-header--null-string-footer--null-string-scheduledat--null-array)

ParameterTypeRequiredDescription`code``string`yesThe instance code.`to``string`yesRecipient phone number.`body``string`yesMain message text.`buttons``array`yesList of button definitions.`header``?string`noHeader text.`footer``?string`noFooter text.`scheduledAt``?string`noSchedule send time.Returns `array`.

```
$callisto->whatsApp()->sendButtons(
    code: 'inst_abc123',
    to: '+2250700000000',
    body: 'Confirm your order?',
    buttons: [
        ['id' => 'yes', 'title' => 'Yes'],
        ['id' => 'no',  'title' => 'No'],
    ],
);
```

#### `sendLocation(string $code, string $to, float $latitude, float $longitude, ?string $name = null, ?string $address = null, ?string $scheduledAt = null): array`

[](#sendlocationstring-code-string-to-float-latitude-float-longitude-string-name--null-string-address--null-string-scheduledat--null-array)

ParameterTypeRequiredDescription`code``string`yesThe instance code.`to``string`yesRecipient phone number.`latitude``float`yesLatitude.`longitude``float`yesLongitude.`name``?string`noLocation name.`address``?string`noLocation address.`scheduledAt``?string`noSchedule send time.Returns `array`.

```
$callisto->whatsApp()->sendLocation(
    code: 'inst_abc123',
    to: '+2250700000000',
    latitude: 5.3599,
    longitude: -4.0083,
    name: 'Acme HQ',
    address: 'Abidjan, Côte d\'Ivoire',
);
```

#### `sendList(string $code, string $to, string $body, string $buttonText, array $sections, ?string $header = null, ?string $footer = null, ?string $scheduledAt = null): array`

[](#sendliststring-code-string-to-string-body-string-buttontext-array-sections-string-header--null-string-footer--null-string-scheduledat--null-array)

ParameterTypeRequiredDescription`code``string`yesThe instance code.`to``string`yesRecipient phone number.`body``string`yesMain message text.`buttonText``string`yesLabel for the list-open button.`sections``array`yesList sections with their rows.`header``?string`noHeader text.`footer``?string`noFooter text.`scheduledAt``?string`noSchedule send time.Returns `array`.

```
$callisto->whatsApp()->sendList(
    code: 'inst_abc123',
    to: '+2250700000000',
    body: 'Pick a plan',
    buttonText: 'View plans',
    sections: [
        [
            'title' => 'Plans',
            'rows'  => [
                ['id' => 'basic', 'title' => 'Basic'],
                ['id' => 'pro',   'title' => 'Pro'],
            ],
        ],
    ],
);
```

---

### `notify()`

[](#notify)

#### `send(string $topic, ?array $email = null, ?array $sms = null, ?array $mobilePush = null, ?array $webPush = null, ?array $webhook = null, ?array $messaging = null, ?array $realTime = null): array`

[](#sendstring-topic-array-email--null-array-sms--null-array-mobilepush--null-array-webpush--null-array-webhook--null-array-messaging--null-array-realtime--null-array)

Multi-channel notification dispatch. **At least one event block must be provided** — calling `send()` with only a `topic` (or with every block empty) throws `ValidationException`.

ParameterTypeRequiredDescription`topic``string`yesNotification topic / template key.`email``array`no\*Email event block.`sms``array`no\*SMS event block.`mobilePush``array`no\*Mobile-push event block.`webPush``array`no\*Web-push event block.`webhook``array`no\*Webhook event block.`messaging``array`no\*Messaging event block.`realTime``array`no\*Real-time event block.\* Each block is individually optional, but **at least one** of them must be present and non-empty.

Returns `array`.

```
$callisto->notify()->send(
    topic: 'order.shipped',
    email: [
        ['to' => 'customer@example.com', 'subject' => 'Your order shipped'],
    ],
    sms: [
        ['to' => '+2250700000000', 'message' => 'Your order is on the way!'],
    ],
);
```

Pagination
----------

[](#pagination)

List endpoints return a [`Paginated`](#paginated) object — a `final readonly` value object with these properties:

PropertyTypeDescription`items``list`The page of typed model objects.`total``int`Total number of items across all pages.`perPage``int`Items per page.`currentPage``int`The current page number.`next``?int`Next page number, or `null` if none.`previous``?int`Previous page number, or `null` if none.`totalPages``int`Total number of pages.```
$page = $callisto->sms()->list(page: 1, perPage: 50);

echo "Page {$page->currentPage} of {$page->totalPages} ({$page->total} total)\n";

foreach ($page->items as $message) {
    // $message is an SmsMessage instance
    echo "{$message->id} → {$message->status}\n";
}

if ($page->next !== null) {
    $next = $callisto->sms()->list(page: $page->next, perPage: 50);
}
```

Typed models
------------

[](#typed-models)

All models live under `Callisto\Sdk\Model`, are declared `final readonly`, and are built via a static `fromArray()` factory. `fromArray()` tolerates missing or extra keys: absent values fall back to `null` (or `''`/`0` for non-nullable scalars), and unknown keys are ignored — so new API fields will not break deserialization.

### `Paginated`

[](#paginated)

See [Pagination](#pagination) above.

### `SmsMessage`

[](#smsmessage)

PropertyType`id``string``senderName``?string``recipient``?string``content``?string``status``?string``createdAt``?string``updatedAt``?string`### `Otp` (model)

[](#otp-model)

`Callisto\Sdk\Model\Otp`. This model carries **both** `otpId` and `id`: `getStatus()` responses populate `otpId`, while `list()` rows populate `id`. Read whichever is relevant to the call you made.

PropertyType`otpId``?string``id``?string``status``?string``recipient``?string``expiresAt``?string``verifiedAt``?string``attempts``?int``createdAt``?string`### `WhatsAppInstance`

[](#whatsappinstance)

PropertyType`id``string``code``?string``clientId``?string``name``?string``phoneNumber``?string``phoneName``?string``status``?string``billingStatus``?string``trialDaysRemaining``?int``monthlyFee``?float``messagesSentToday``?int``messagesSentMonth``?int``dailyLimit``?int``lastMessageAt``?string``webhookUrl``?string``isActive``?bool``createdAt``?string``updatedAt``?string`### `WhatsAppMessage`

[](#whatsappmessage)

PropertyType`id``string``instanceId``?string``clientId``?string``apiClientId``?string``recipient``?string``recipientName``?string``messageType``?string``content``?string``mediaUrl``?string``mediaMimetype``?string``mediaFilename``?string``extraData``?array``direction``?string``status``?string``whatsappMessageId``?string``errorCode``?int``errorMessage``?string``retryCount``?int``isBillable``?bool``cost``?float``sentAt``?string``deliveredAt``?string``readAt``?string``scheduledAt``?string``createdAt``?string``updatedAt``?string``processorIdentifier``?string`Enums
-----

[](#enums)

All enums live under `Callisto\Sdk\Enum` and are backed by strings. Anywhere an enum is accepted you may also pass its raw string value.

### `OtpType`

[](#otptype)

CaseValue`Digit``'digit'``Alpha``'alpha'``Alphanumeric``'alphanumeric'`### `OtpProvider`

[](#otpprovider)

CaseValue`Sms``'sms'``Whatsapp``'whatsapp'`### `WhatsAppMediaType`

[](#whatsappmediatype)

CaseValue`Image``'image'``Video``'video'``Document``'document'``Audio``'audio'`Error handling
--------------

[](#error-handling)

All SDK errors extend `Callisto\Sdk\Exception\CallistoException`, which exposes:

- `getMessage(): string` — the error message (from the API `message` field when present, else `HTTP `).
- `getStatusCode(): int` — the HTTP status code (`0` for transport-level failures).
- `getBody(): mixed` — the decoded JSON response body, when available.

HTTP responses are mapped to specific subclasses:

ExceptionWhen`AuthenticationException`HTTP `401``ValidationException`HTTP `400` or `422` (also thrown client-side for invalid input)`NotFoundException`HTTP `404``RateLimitException`HTTP `429` — adds `getRetryAfter(): ?int` (seconds, from the `Retry-After` header)`ApiException`Any other `>= 400` status`NetworkException`Transport failure (DNS, connection, timeout)```
use Callisto\Sdk\Exception\AuthenticationException;
use Callisto\Sdk\Exception\ValidationException;
use Callisto\Sdk\Exception\NotFoundException;
use Callisto\Sdk\Exception\RateLimitException;
use Callisto\Sdk\Exception\NetworkException;
use Callisto\Sdk\Exception\CallistoException;

try {
    $callisto->sms()->send(
        sender: 'Acme',
        to: '+2250700000000',
        message: 'Hello!',
    );
} catch (RateLimitException $e) {
    $wait = $e->getRetryAfter() ?? 1;
    sleep($wait);
    // ...retry
} catch (ValidationException $e) {
    echo "Invalid request: {$e->getMessage()}";
    var_dump($e->getBody());
} catch (AuthenticationException $e) {
    echo 'Check your client ID and API key.';
} catch (NotFoundException $e) {
    echo 'Resource not found.';
} catch (NetworkException $e) {
    echo "Network problem: {$e->getMessage()}";
} catch (CallistoException $e) {
    // Catch-all for any remaining API error (ApiException, etc.)
    echo "API error ({$e->getStatusCode()}): {$e->getMessage()}";
}
```

Error reporting
---------------

[](#error-reporting)

The SDK ships an opt-in, Sentry-style error reporter that POSTs captured errors to a Callisto error-tracking **ingest endpoint**. It auto-captures the SDK's own `CallistoException`s (API + network + client-side validation) and exposes a public API so your application can report its own exceptions. Reporting is **fully disabled** unless a DSN is configured.

> **Delivery is synchronous best-effort (PHP-specific).** PHP has no portable background threads in a request context, so the reporter delivers each event **inline with a short timeout (2s)**. Every failure — any exception, any non-202 — is swallowed. Reporting never alters or blocks the original error path, and `captureException`/`captureMessage` never throw.

### Enabling

[](#enabling)

Pass a DSN (constructor argument or env var). The DSN **is** the full ingest URL (`{APP_URL}/ingest/{id}?key={public_key}`):

```
use Callisto\Sdk\Client;

$callisto = new Client(
    clientId: 'your-client-id',
    apiKey: 'your-api-key',
    errorDsn: 'https://app.callistosignal.com/ingest/?key=', // enables reporting
    captureUnhandled: true,        // optional, default false — install global handler
    environment: 'production',     // optional, tagged in context.environment
);
```

### Environment variables

[](#environment-variables)

When the corresponding argument is omitted (or `null`), it is resolved from the environment:

ArgumentEnv varDefaultMeaning`errorDsn``CALLISTO_ERROR_DSN`noneIngest DSN. Absent (or not a valid URL) → reporting disabled (no-op).`captureUnhandled``CALLISTO_CAPTURE_UNHANDLED``false`Install the global unhandled-exception / fatal handler.`environment``CALLISTO_ENVIRONMENT`noneOptional tag included in `context.environment`.### Public API

[](#public-api)

```
// Report your own exceptions and messages.
$callisto->captureException($throwable, level: 'error', extra: ['feature' => 'checkout']);
$callisto->captureMessage('payment retried', level: 'info');

// Attach user context to subsequent events (pass null to clear).
$callisto->setUser(['id' => 'u-123', 'email' => 'user@example.com']);

// Best-effort flush (no-op for the synchronous PHP reporter).
$callisto->flush();

// Advanced: the reporter itself.
$reporter = $callisto->errorReporter();
```

`level` is constrained to `fatal | error | warning | info` (anything else falls back to `error`).

### Opt-in global handler

[](#opt-in-global-handler)

When `captureUnhandled` is `true` **and** a DSN is set, the client installs `set_exception_handler` and a `register_shutdown_function` (for fatal errors) that report at level `fatal`. Both **chain** any previously-registered handler / preserve PHP's default behavior (the exception is still re-raised), so they never clobber existing error handling.

### PII guarantee

[](#pii-guarantee)

The reporter **never transmits** your `clientId`, `apiKey`, the `Authorization` header, or the **outgoing request body** (which carries phone numbers and message content). Only the server's error response `body`, `status_code`, the HTTP `method`, and the request `path` may leave the process. This is enforced and covered by tests.

###  Health Score

20

—

LowBetter than 13% of packages

Maintenance64

Regular maintenance activity

Popularity0

Limited adoption so far

Community6

Small or concentrated contributor base

Maturity11

Early-stage or recently created project

 Bus Factor1

Top contributor holds 100% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

### Community

Maintainers

![](https://www.gravatar.com/avatar/96099e66c63e31445f3f76a12e94104030f504eeb18f007216bb4ebdcdeadf7f?d=identicon)[papac](/maintainers/papac)

---

Top Contributors

[![papac](https://avatars.githubusercontent.com/u/9353811?v=4)](https://github.com/papac "papac (5 commits)")

### Embed Badge

![Health badge](/badges/callisto-php-sdk/health.svg)

```
[![Health](https://phpackages.com/badges/callisto-php-sdk/health.svg)](https://phpackages.com/packages/callisto-php-sdk)
```

###  Alternatives

[facebook/php-business-sdk

PHP SDK for Facebook Business

90923.5M35](/packages/facebook-php-business-sdk)[exsyst/swagger

A php library to manipulate Swagger specifications

35916.3M7](/packages/exsyst-swagger)[hubspot/api-client

Hubspot API client

24015.5M18](/packages/hubspot-api-client)[botman/driver-telegram

Telegram driver for BotMan

93452.6k6](/packages/botman-driver-telegram)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
