PHPackages                             john-wink/telli-laravel-wrapper - 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. john-wink/telli-laravel-wrapper

ActiveLibrary[API Development](/categories/api)

john-wink/telli-laravel-wrapper
===============================

A Laravel wrapper for the telli API v2 - AI phone call agents

v0.2.0(1mo ago)02MITPHPPHP ^8.3CI passing

Since Jul 11Pushed 1mo agoCompare

[ Source](https://github.com/john-wink/telli-laravel-wrapper)[ Packagist](https://packagist.org/packages/john-wink/telli-laravel-wrapper)[ Docs](https://github.com/john-wink/telli-laravel-wrapper)[ RSS](/packages/john-wink-telli-laravel-wrapper/feed)WikiDiscussions main Synced 1w ago

READMEChangelog (3)Dependencies (13)Versions (4)Used By (0)

Telli Laravel Wrapper
=====================

[](#telli-laravel-wrapper)

[![Latest Version on Packagist](https://camo.githubusercontent.com/caa07db20b9c76a908352749ee5538de8736d9bba59a69ed442f6be940a6d161/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f762f6a6f686e2d77696e6b2f74656c6c692d6c61726176656c2d777261707065722e7376673f7374796c653d666c61742d737175617265)](https://packagist.org/packages/john-wink/telli-laravel-wrapper)[![GitHub Tests Action Status](https://camo.githubusercontent.com/1ab5e4e690a6d1bbbcad87f56b5698bfd3ee2d53cc7c8eda6cba5c670c6e46aa/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f616374696f6e732f776f726b666c6f772f7374617475732f6a6f686e2d77696e6b2f74656c6c692d6c61726176656c2d777261707065722f72756e2d74657374732e796d6c3f6272616e63683d6d61696e266c6162656c3d7465737473267374796c653d666c61742d737175617265)](https://github.com/john-wink/telli-laravel-wrapper/actions/workflows/run-tests.yml)[![Total Downloads](https://camo.githubusercontent.com/45214659b276e945c5fb0ec1e1612b24a99bb820e2b204e0866bbe5b083c6952/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f64742f6a6f686e2d77696e6b2f74656c6c692d6c61726176656c2d777261707065722e7376673f7374796c653d666c61742d737175617265)](https://packagist.org/packages/john-wink/telli-laravel-wrapper)

A Laravel wrapper for the [telli](https://telli.com) API - AI phone call agents. Covers the v2 contacts/properties/agents API and the v1 calls, dialer and webhook surface. Fully typed DTOs via spatie/laravel-data, a typed exception per documented API error code, cursor pagination as lazy collections, and an idempotent contact create.

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

[](#requirements)

- PHP 8.3+
- Laravel 12

Installation
------------

[](#installation)

```
composer require john-wink/telli-laravel-wrapper

```

Publish the config file (optional):

```
php artisan vendor:publish --tag="telli-config"

```

Publish the translations (optional, ships with `en` and `de`):

```
php artisan vendor:publish --tag="telli-translations"

```

Set your API key in `.env`:

```
TELLI_API_KEY=your-api-key

```

Usage
-----

[](#usage)

### Contacts

[](#contacts)

```
use JohnWink\Telli\Facades\Telli;
use JohnWink\Telli\Data\CreateContactData;
use JohnWink\Telli\Data\UpdateContactData;
use JohnWink\Telli\Data\ContactPropertyInputData;
use Spatie\LaravelData\DataCollection;

$contact = Telli::contacts()->create(new CreateContactData(
    firstName: 'Max',
    lastName: 'Mustermann',
    phoneNumber: '+4915112345678',
    externalId: 'lead-4711',
    properties: new DataCollection(ContactPropertyInputData::class, [
        new ContactPropertyInputData(key: 'lead_score', value: 87),
    ]),
));

$page = Telli::contacts()->list(limit: 100);          // one page + pageInfo/meta
Telli::contacts()->all()->each(fn ($contact) => ...); // lazy, follows the cursor

$contact = Telli::contacts()->get($id);
$contact = Telli::contacts()->getByExternalId('lead-4711');
$contact = Telli::contacts()->update($id, new UpdateContactData(email: 'new@example.com'));
Telli::contacts()->delete($id);

```

**Replace semantics:** `Telli::contacts()->replace($id, $data)` issues a `PUT`. Optional fields you omit are reset to `null` (respectively `[]` for properties) on the server - unlike `update()`, which only touches the fields you send.

**Idempotent create:** when `externalId` is set and the API answers with a 5xx or the connection drops, the wrapper looks the contact up by external ID before retrying - you never create duplicates. Without an `externalId` there is nothing to check against, so a failed create is NOT retried; set an `externalId` whenever you can.

### Contact properties

[](#contact-properties)

```
use JohnWink\Telli\Data\CreateContactPropertyData;
use JohnWink\Telli\Data\UpdateContactPropertyData;
use JohnWink\Telli\Enums\PropertyDataType;

$properties = Telli::contactProperties()->list();
$property = Telli::contactProperties()->create(new CreateContactPropertyData(
    key: 'lead_score',
    dataType: PropertyDataType::Number,
    label: 'Lead Score',
));
$property = Telli::contactProperties()->get('lead_score');
$property = Telli::contactProperties()->update('lead_score', new UpdateContactPropertyData(label: 'Score'));

```

### Agents &amp; account health

[](#agents--account-health)

```
$agents = Telli::agents()->all();          // lazy collection over all pages
$agent = Telli::agents()->get($agentId);
$health = Telli::account()->health();      // AccountHealthStatus::Operational|Degraded

```

### Calls (v1 API)

[](#calls-v1-api)

telli's call endpoints live on the v1 API (v2 does not cover calls yet); the wrapper maps them to the same camelCase PHP API:

```
use JohnWink\Telli\Data\Calls\ScheduleCallData;
use JohnWink\Telli\Data\Calls\ScheduleData;

$result = Telli::calls()->schedule(new ScheduleCallData(
    contactId: $contact->id,
    agentId: $agentId,
));                                            // respects the dialer window

Telli::calls()->schedule(new ScheduleCallData(
    contactId: $contact->id,
    agentId: $agentId,
    schedule: ScheduleData::for(now()->addHours(2)),
));                                            // scheduled for a specific time

$call = Telli::calls()->get($callId);          // transcript, analysis, booked_slot_for, recording
$page = Telli::calls()->list(contactId: $contact->id);

Telli::calls()->initiate(...);                 // exists, but telli recommends schedule() -
                                               // initiate() calls even outside business hours

```

`Telli::dialer()->remove($contactId)` takes a contact out of the auto-dialer loop; `Telli::phoneNumbers()` manages SIP numbers. A 402 response (insufficient funds) raises a typed `PaymentRequiredException`.

`Telli::account()->verifyApiKey()` returns whether the configured key is valid - handy for settings screens.

### Webhooks

[](#webhooks)

telli signs webhooks with Svix. Verify and parse them without extra dependencies:

```
use JohnWink\Telli\Facades\Telli;

Telli::webhooks()->verify(
    payload: $request->getContent(),
    svixId: $request->header('svix-id'),
    svixTimestamp: $request->header('svix-timestamp'),
    svixSignature: $request->header('svix-signature'),
    secret: $connection->webhook_secret,
);                                             // throws TelliWebhookSignatureException on mismatch

$event = Telli::webhooks()->parse($request->json()->all());

if ($call = $event->call()) {                  // call_ended events
    $call->transcript; $call->bookedSlotFor; $call->recordingUrl;
}

```

### Multi-tenant / per-team API keys

[](#multi-tenant--per-team-api-keys)

The config key is just the default account. Scope any call to a different telli account at runtime - `withApiKey()` returns a NEW client and never mutates the shared singleton, so there is no key bleed between requests, queued jobs or Octane workers:

```
Telli::withApiKey($team->telli_api_key)->contacts()->create($data);

```

Error handling
--------------

[](#error-handling)

Every documented telli error code maps to its own exception with typed fields, all extending `TelliRequestException` (which extends `TelliException`):

```
use JohnWink\Telli\Exceptions\ContactNotFoundException;
use JohnWink\Telli\Exceptions\TelliValidationException;
use JohnWink\Telli\Exceptions\TelliException;

try {
    Telli::contacts()->get($id);
} catch (ContactNotFoundException $exception) {
    $exception->contactId;      // typed payload from the API
} catch (TelliValidationException $exception) {
    $exception->issues;         // list of ValidationIssueData (code, message, path)
} catch (TelliException $exception) {
    // configuration, connection or any other API error
}

```

Exception messages are translated (`en`, `de`); the raw API message stays available via `$exception->apiMessage`, the raw code via `$exception->rawCode`.

Retries
-------

[](#retries)

429 responses are retried for every request; connection errors and 5xx responses are retried for non-POST requests only — a blind POST retry could create duplicates. Failed POST creates recover through the verify-then-retry mechanism instead (see "Idempotent create" above) when a natural key (`externalId` / property `key`) is present. The backoff is configurable (`config/telli.php`); a `Retry-After` header on 429 responses takes precedence.

Testing your integration
------------------------

[](#testing-your-integration)

The wrapper uses Laravel's HTTP client, so `Http::fake()` works out of the box:

```
Http::fake(['api.telli.com/*' => Http::response([...])]);

```

Or swap the whole client with a mock:

```
use JohnWink\Telli\Facades\Telli;

Telli::swap($mock);

```

Changelog
---------

[](#changelog)

See [CHANGELOG](CHANGELOG.md).

License
-------

[](#license)

MIT, see [LICENSE](LICENSE.md).

###  Health Score

36

—

LowBetter than 79% of packages

Maintenance90

Actively maintained with recent releases

Popularity2

Limited adoption so far

Community6

Small or concentrated contributor base

Maturity41

Maturing project, gaining track record

 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.

###  Release Activity

Cadence

Every ~0 days

Total

3

Last Release

50d ago

### Community

Maintainers

![](https://www.gravatar.com/avatar/141d739e08a4293474c8598c867ff954a537c0bfd022e758ac183528c0f5cfb6?d=identicon)[john-wink](/maintainers/john-wink)

---

Top Contributors

[![john-wink](https://avatars.githubusercontent.com/u/35034627?v=4)](https://github.com/john-wink "john-wink (15 commits)")

---

Tags

apilaravelsdkwrapperjohn-winkvoice-aitelli

###  Code Quality

TestsPest

Static AnalysisPHPStan

Code StyleLaravel Pint

### Embed Badge

![Health badge](/badges/john-wink-telli-laravel-wrapper/health.svg)

```
[![Health](https://phpackages.com/badges/john-wink-telli-laravel-wrapper/health.svg)](https://phpackages.com/packages/john-wink-telli-laravel-wrapper)
```

###  Alternatives

[psalm/plugin-laravel

Psalm plugin for Laravel

3365.5M359](/packages/psalm-plugin-laravel)[defstudio/telegraph

A laravel facade to interact with Telegram Bots

818355.4k3](/packages/defstudio-telegraph)[laravel/horizon

Dashboard and code-driven configuration for Laravel queues.

4.2k104.7M370](/packages/laravel-horizon)[laravel/cashier

Laravel Cashier provides an expressive, fluent interface to Stripe's subscription billing services.

2.5k31.8M166](/packages/laravel-cashier)[laravel/mcp

Rapidly build MCP servers for your Laravel applications.

80732.6M270](/packages/laravel-mcp)[simplestats-io/laravel-client

Server-side analytics for Laravel that follows the full funnel from visit to registration to payment, attributed to the channel that drove it. Revenue, MRR, churn and ad-spend profit (ROAS/CAC) per channel. GDPR compliant, ad-blocker proof.

5226.7k](/packages/simplestats-io-laravel-client)

PHPackages © 2026

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