PHPackages                             codebar-ag/laravel-docuware - 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. codebar-ag/laravel-docuware

ActiveLibrary[API Development](/categories/api)

codebar-ag/laravel-docuware
===========================

DocuWare integration with Laravel

v14.0.0(2mo ago)1125.1k8[1 issues](https://github.com/codebar-ag/laravel-docuware/issues)[1 PRs](https://github.com/codebar-ag/laravel-docuware/pulls)MITPHPPHP 8.4.\*|8.5.\*CI passing

Since Apr 6Pushed 1mo ago1 watchersCompare

[ Source](https://github.com/codebar-ag/laravel-docuware)[ Packagist](https://packagist.org/packages/codebar-ag/laravel-docuware)[ Docs](https://github.com/codebar-ag/laravel-docuware)[ RSS](/packages/codebar-ag-laravel-docuware/feed)WikiDiscussions main Synced 2w ago

READMEChangelog (10)Dependencies (69)Versions (85)Used By (0)

[![](https://camo.githubusercontent.com/4bf4225c3a9875c26a21fd07b734caaaf83798321885f70528f25cb2ab07dbed/68747470733a2f2f62616e6e6572732e6265796f6e64636f2e64652f4c61726176656c253230446f6375576172652e706e673f7468656d653d6c69676874267061636b6167654d616e616765723d636f6d706f7365722b72657175697265267061636b6167654e616d653d636f64656261722d61672532466c61726176656c2d646f637577617265267061747465726e3d63697263756974426f617264267374796c653d7374796c655f31266465736372697074696f6e3d416e2b6f70696e696f6e617465642b7761792b746f2b696e746567726174652b446f6375576172652b776974682b4c61726176656c266d643d312673686f7757617465726d61726b3d3026666f6e7453697a653d313735707826696d616765733d646f63756d656e742d7265706f7274)](https://camo.githubusercontent.com/4bf4225c3a9875c26a21fd07b734caaaf83798321885f70528f25cb2ab07dbed/68747470733a2f2f62616e6e6572732e6265796f6e64636f2e64652f4c61726176656c253230446f6375576172652e706e673f7468656d653d6c69676874267061636b6167654d616e616765723d636f6d706f7365722b72657175697265267061636b6167654e616d653d636f64656261722d61672532466c61726176656c2d646f637577617265267061747465726e3d63697263756974426f617264267374796c653d7374796c655f31266465736372697074696f6e3d416e2b6f70696e696f6e617465642b7761792b746f2b696e746567726174652b446f6375576172652b776974682b4c61726176656c266d643d312673686f7757617465726d61726b3d3026666f6e7453697a653d313735707826696d616765733d646f63756d656e742d7265706f7274)

[![Latest Version on Packagist](https://camo.githubusercontent.com/5741a7d9912f58a5dd57b71327acda1eedd78173ba77db1d9db4add90a40f905/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f762f636f64656261722d61672f6c61726176656c2d646f6375776172652e7376673f7374796c653d666c61742d737175617265)](https://packagist.org/packages/codebar-ag/laravel-docuware)[![Total Downloads](https://camo.githubusercontent.com/b0e4d8d7407c2a516e03d045b00bad2dd79780512e88a7cc06cd28025441193c/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f64742f636f64656261722d61672f6c61726176656c2d646f6375776172652e7376673f7374796c653d666c61742d737175617265)](https://packagist.org/packages/codebar-ag/laravel-docuware)[![GitHub-Tests](https://github.com/codebar-ag/laravel-docuware/actions/workflows/run-tests.yml/badge.svg?branch=main)](https://github.com/codebar-ag/laravel-docuware/actions/workflows/run-tests.yml)[![GitHub Code Style](https://github.com/codebar-ag/laravel-docuware/actions/workflows/fix-php-code-style-issues.yml/badge.svg?branch=main)](https://github.com/codebar-ag/laravel-docuware/actions/workflows/fix-php-code-style-issues.yml)[![PHPStan](https://github.com/codebar-ag/laravel-docuware/actions/workflows/phpstan.yml/badge.svg)](https://github.com/codebar-ag/laravel-docuware/actions/workflows/phpstan.yml)

An opinionated, modern integration for the [DocuWare Platform REST API](https://developer.docuware.com/rest/index.html). You work with **resources** and **immutable data objects** — never raw HTTP.

> This package is **not** designed as a replacement for the official DocuWare REST API. It is an opinionated convenience layer on top of it.

---

Table of contents
-----------------

[](#table-of-contents)

- [Highlights](#highlights)
- [Requirements](#requirements)
- [Installation](#installation)
- [Configuration](#configuration)
- [Authentication &amp; grants](#authentication--grants)
- [Connecting (config file or runtime DTO)](#connecting-config-file-or-runtime-dto)
- [Quickstart](#quickstart)
- [Resources](#resources)
- [Search builder](#search-builder)
- [Index field values](#index-field-values)
- [Immutable data &amp; withers](#immutable-data--withers)
- [Enums](#enums)
- [Encrypted document URLs](#encrypted-document-urls)
- [Endpoint catalog](#endpoint-catalog)
- [Data object (DTO) reference](#data-object-dto-reference)
- [Caching](#caching)
- [Error handling](#error-handling)
- [Events](#events)
- [Security](#security)
- [Testing](#testing)
- [Credits](#credits)
- [License](#license)

Highlights
----------

[](#highlights)

- 🎯 **Small, domain-shaped API** — `DocuWare::documents($cabinet)->search()->cursor()`.
- 🧊 **Immutable data** via [`spatie/laravel-data`](https://spatie.be/docs/laravel-data) with `->copyWith(...)` withers.
- 🏢 **Multi-tenant first-class** — by config name **or** a runtime connection DTO.
- 🔢 **Typed index fields** — `IndexFields::make()->text(...)->number(...)->date(...)->table(...)` converts PHP values into the exact DocuWare wire format for you.
- 🔒 **Secure by default** — secrets are never logged, evented, or thrown.
- ♻️ **Resilient** — encrypted + lock-guarded token store, retry with backoff honoring `Retry-After`.
- 🧯 **One error model** — every failure descends from `DocuWareException`.
- 🧪 **Fully fakeable &amp; typed** — PHPStan max, offline fixture replay.

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

[](#requirements)

VersionPHPLaravelDocuWare Cloud**v14**^8.4 – ^8.513.\*✅v13^8.3 – ^8.413.\*✅v12^8.2 – ^8.412.\*✅v11 (alpha)^8.211.\*✅&gt; v4.0^8.211.\*✅&gt; v3.0^8.210.\*✅&gt; v2.0^8.19.\*✅&gt; v1.2^8.19.\*✅&lt; v1.2^8.08.\*✅> **v14 raises the minimum PHP to 8.4.** Laravel 13 stays the target framework.

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

[](#installation)

```
composer require codebar-ag/laravel-docuware
php artisan vendor:publish --tag=laravel-docuware-config
```

Configure the default instance in `.env`:

```
DOCUWARE_URL=https://your-instance.docuware.cloud
DOCUWARE_USERNAME=your-user
DOCUWARE_PASSWORD=your-password
DOCUWARE_PASSPHRASE=your-url-passphrase
```

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

[](#configuration)

The default instance reads the flat `DOCUWARE_*` env keys above. Everything else has sane defaults; publish the config to tune caching, retries, rate limiting, and multi-tenant instances.

**Full config reference (`config/laravel-docuware.php`)**KeyEnvDefaultPurpose`default``DOCUWARE_INSTANCE``default`Which instance bare facade calls use`platform_path``DOCUWARE_PLATFORM_PATH``DocuWare/Platform`REST base path appended to the URL`timeout``DOCUWARE_TIMEOUT``15`Default request timeout (seconds)`passphrase``DOCUWARE_PASSPHRASE`—Passphrase for encrypted document URLs`instances.*`——Named multi-tenant connections (see below)`credentials.url``DOCUWARE_URL`—Legacy flat URL for the `default` instance`credentials.username``DOCUWARE_USERNAME`—Legacy flat username`credentials.password``DOCUWARE_PASSWORD`—Legacy flat password`configurations.client_id``DOCUWARE_CLIENT_ID``docuware.platform.net.client`OAuth client id`configurations.scope``DOCUWARE_SCOPE``docuware.platform`OAuth scope`configurations.cache.driver``DOCUWARE_CACHE_DRIVER``file`Cache store for responses &amp; tokens`configurations.cache.lifetime_in_seconds``DOCUWARE_CACHE_LIFETIME_IN_SECONDS``60`Cache TTL for `Cacheable` requests`configurations.request.timeout_in_seconds``DOCUWARE_TIMEOUT``60`Per-request timeout`debug.capture_bodies``DOCUWARE_DEBUG_CAPTURE_BODIES``false`Attach (redacted) bodies to `ResponseReceived``retry.enabled``DOCUWARE_RETRY_ENABLED``true`Exponential backoff on transient failures`retry.times``DOCUWARE_RETRY_TIMES``3`Max retry attempts`retry.base_interval_ms``DOCUWARE_RETRY_BASE_INTERVAL_MS``250`First backoff interval`retry.max_interval_ms``DOCUWARE_RETRY_MAX_INTERVAL_MS``10000`Backoff ceiling`rate_limit.enabled``DOCUWARE_RATE_LIMIT_ENABLED``false`Per-instance client-side rate limiting`rate_limit.allow``DOCUWARE_RATE_LIMIT_ALLOW``60`Requests allowed per window`rate_limit.per_seconds``DOCUWARE_RATE_LIMIT_PER_SECONDS``60`Window length (seconds)Authentication &amp; grants
---------------------------

[](#authentication--grants)

OAuth is handled for you: the package discovers the identity service, exchanges credentials for an access token, encrypts it, caches it per instance, and refreshes it under a lock before it expires. You never call the token endpoint yourself.

Three grants are supported:

Grant`grant` valueNeedsUse caseCredentials`credentials`url, username, passwordStandard username/password loginTrusted user`trusted_user`url, username, password, impersonateAuthenticate as a trusted user, act as anotherDocuWare token`token`url, tokenExchange a DocuWare login token (`dwtoken`)Connecting (config file or runtime DTO)
---------------------------------------

[](#connecting-config-file-or-runtime-dto)

There are two ways to connect — pick whichever fits your tenancy model.

### 1. Named instances in config

[](#1-named-instances-in-config)

Best when tenants are known ahead of time. Add them to `config/laravel-docuware.php`:

```
'instances' => [
    'acme'   => ['grant' => 'credentials', 'url' => '…', 'username' => '…', 'password' => '…'],
    'globex' => ['grant' => 'token', 'url' => '…', 'token' => '…'],
],
```

```
DocuWare::instance('acme')->documents($cabinetId)->search()->get();
```

### 2. A runtime connection DTO (database-driven multi-tenancy)

[](#2-a-runtime-connection-dto-database-driven-multi-tenancy)

Multi-tenancy is **not** limited to config files. When connections live in your database — the common case — build a typed config DTO at runtime and hand it to `DocuWare::connection()`:

```
use CodebarAg\DocuWare\Facades\DocuWare;
use CodebarAg\DocuWare\Config\CredentialsConfig;

$client = DocuWare::connection(CredentialsConfig::for(
    url:      $tenant->docuware_url,
    username: $tenant->docuware_user,
    password: $tenant->docuware_secret,
    name:     $tenant->slug,            // namespaces this tenant's cache + token entry
));

$client->documents($cabinetId)->search()->fullText('invoice')->get();
```

Each grant has a typed `for()` factory that fills sensible defaults (cache driver, lifetime, timeout, client id, scope) — override any of them as named arguments:

```
use CodebarAg\DocuWare\Config\{CredentialsConfig, TokenConfig, TrustedUserConfig};

CredentialsConfig::for(url: '…', username: '…', password: '…', name: 'acme');
TokenConfig::for(url: '…', token: '…', name: 'globex');
TrustedUserConfig::for(url: '…', username: '…', password: '…', impersonate: 'bob', name: 'acme');
```

`DocuWare::instance(...)` also accepts a DTO directly, so `DocuWare::instance($config)` and `DocuWare::connection($config)` are equivalent. Runtime connections are **not** name-cached, so two tenants may safely reuse a `name`; their tokens stay isolated by URL + credentials.

Every connection is independent: its own connector, cache namespace, encrypted token entry, and rate limiter.

Quickstart
----------

[](#quickstart)

```
use CodebarAg\DocuWare\Facades\DocuWare;
use CodebarAg\DocuWare\Data\Documents\DocumentData;
use CodebarAg\DocuWare\Data\Write\IndexFields;
use CodebarAg\DocuWare\Enums\TargetFileType;

// Lazily iterate EVERY matching document across all pages (memory-safe).
DocuWare::documents($cabinetId)->search()
    ->fullText('invoice')
    ->where('STATUS', 'open')
    ->whereDateBetween('DWSTOREDATETIME', $from, $to)
    ->latest('DWSTOREDATETIME')
    ->cursor()
    ->each(fn (DocumentData $document) => /* … */);

$document = DocuWare::documents($cabinetId)->find($documentId);

$document = DocuWare::documents($cabinetId)->store(
    fileContent: file_get_contents('invoice.pdf'),
    fileName: 'invoice.pdf',
    indexes: IndexFields::make()->text('STATUS', 'open')->number('AMOUNT', 42),
);

DocuWare::documents($cabinetId)->update($documentId, IndexFields::make()->text('STATUS', 'closed'));
$pdf = DocuWare::documents($cabinetId)->download($documentId, TargetFileType::PDF);
DocuWare::documents($cabinetId)->delete($documentId);
```

Resources
---------

[](#resources)

Every resource is reached from the `DocuWare` facade (default instance) or from a resolved client (`DocuWare::instance('acme')->…`, `DocuWare::connection($config)->…`). Each returns immutable `*Data`objects or collections of them.

### Documents — `DocuWare::documents($cabinetId)`

[](#documents--docuwaredocumentscabinetid)

The documents resource is broad, so its methods are grouped by area below.

**Core (search, read, write, lifecycle)**

MethodReturnsDescription`search()``SearchQuery`Fluent search builder (see below)`find($id)``DocumentData`A single document`count()``int`Total documents in the cabinet`store($fileContent, $fileName, $indexes, $storeDialogId)``DocumentData`Create a data record`update($id, $indexes, $forceUpdate = false)``Collection`Update index values`delete($id)`—Delete a document`download($id, TargetFileType = AUTO, $keepAnnotations = false)``string`Binary content`preview($id)``string`Preview image bytes**Sections** (a document is composed of one or more file sections)

MethodReturnsDescription`sections($id)` / `section($sectionId)``Collection` / `SectionData`List / fetch sections`deleteSection($sectionId)``bool`Delete a section`downloadSection($sectionId)` / `textshot($sectionId)``string`Section data / text layer`thumbnail($sectionId, $page = 0)``string`Thumbnail bytes**Annotations &amp; application properties**

MethodReturnsDescription`annotations($id)` / `annotate($id, $payload)` / `stamps($id)``Collection` / `mixed` / `Collection`Annotations &amp; stamps`applicationProperties($id)` / `addApplicationProperties(...)` / `updateApplicationProperties(...)` / `deleteApplicationProperties(...)``mixed`App-defined properties**Structure operations (clip/staple, transfer, batch &amp; upload)**

MethodReturnsDescription`clip($ids, $force)` / `staple($ids, $force)` / `unclip($id)` / `unstaple($id)``DocumentData` / `DocumentPageData`Content merge/divide`transfer($id, $destinationFileCabinetId, $storeDialogId, $keepSource = false)``bool`Move/copy across cabinets`batchUpdate(...)` / `appendPdf(...)` / `appendFiles(...)` / `replaceSection(...)``array` / `Section` / `Document`Batch &amp; upload operations`workflowHistory($id)``Collection`Document workflow history### File cabinets &amp; structure — `fileCabinets`, `dialogs`, `selectLists`

[](#file-cabinets--structure--filecabinets-dialogs-selectlists)

Cabinet metadata, the search/store dialogs defined on a cabinet, and the option lists behind select fields.

```
DocuWare::fileCabinets()->all();                 // Collection
DocuWare::fileCabinets()->info($cabinetId);      // FileCabinetInformationData
DocuWare::fileCabinets()->fields($cabinetId);    // Collection — the cabinet's index fields

DocuWare::dialogs($cabinetId)->all();            // Collection
DocuWare::dialogs($cabinetId)->ofType(DialogType::SEARCH);
DocuWare::dialogs($cabinetId)->find($dialogId);

DocuWare::selectLists($cabinetId)->get($dialogId, 'FIELD');
DocuWare::selectLists($cabinetId)->filtered($dialogId, 'FIELD', $expression);
```

### Organization &amp; users — `organizations`, `users`, `groups`, `roles`

[](#organization--users--organizations-users-groups-roles)

Tenant-level directory: the organization, its users, and the groups/roles they belong to.

```
DocuWare::organizations()->all();                // Collection
DocuWare::organizations()->loginToken($targetProducts, $usage, $lifetime);

DocuWare::users()->all();                        // Collection
DocuWare::users()->find($userId);
DocuWare::users()->ofGroup($groupId);
DocuWare::users()->ofRole($roleId);
DocuWare::users()->create($user);                // UserInput
DocuWare::users()->update($user);
DocuWare::users()->addToGroup($userId, $groupIds);
DocuWare::users()->removeFromGroup($userId, $groupIds);
DocuWare::users()->addToRole($userId, $roleIds);
DocuWare::users()->removeFromRole($userId, $roleIds);
DocuWare::users()->groupsOf($userId);
DocuWare::users()->rolesOf($userId);

DocuWare::groups()->all();                       // Collection
DocuWare::roles()->all();                        // Collection
```

### Workflows — `workflows`

[](#workflows--workflows)

```
DocuWare::workflows()->historySteps($workflowId, $instanceId);  // InstanceHistoryData
```

### Trash (recycle bin) — `trash`

[](#trash-recycle-bin--trash)

Documents deleted from a cabinet land here until purged or restored.

```
DocuWare::trash()->search($page, $perPage, ...); // TrashPageData
DocuWare::trash()->delete($ids);                 // DeleteDocumentsData — purge permanently
DocuWare::trash()->restore($ids);                // RestoreDocumentsData — recover to the cabinet
```

Search builder
--------------

[](#search-builder)

`DocuWare::documents($cabinetId)->search()` returns a fluent `SearchQuery`. Conditions and ordering are chainable; a terminal method runs the request.

```
DocuWare::documents($cabinetId)->search()
    ->fileCabinets([$cabinetId, $otherCabinetId])  // search across multiple cabinets
    ->dialog($searchDialogId)                       // optional explicit search dialog
    ->fullText('term')
    ->where('FIELD', 'value')
    ->whereIn('FIELD', ['a', 'b'])
    ->whereEmpty('FIELD')
    ->whereNotEmpty('FIELD')
    ->whereDate('DATE_FIELD', '>=', $carbon)
    ->whereDateBetween('DATE_FIELD', $from, $to)
    ->orderBy('FIELD', 'desc')        // or ->latest('FIELD') / ->oldest('FIELD')
    ->page(1)->perPage(50);
```

TerminalReturnsDescription`get()``DocumentPageData`One page of results`first()``?DocumentData`First match, or `null``count()``int`Total matching documents`cursor()``LazyCollection`Memory-safe iteration over **all** pagesIndex field values
------------------

[](#index-field-values)

DocuWare expects each index value in a specific wire shape (`FieldName` / `Item` / `ItemElementName`). You don't build that by hand — pass PHP values to the `IndexFields` builder and it converts each one to the correct typed field. This is the answer to *"how do I pass an int / string / table and get the right index field?"*

```
use CodebarAg\DocuWare\Data\Write\IndexFields;
use Illuminate\Support\Carbon;

$indexes = IndexFields::make()
    ->text('STATUS', 'open')            // String
    ->memo('NOTES', 'long text…')       // Memo
    ->keyword('TAGS', 'invoice')        // Keyword
    ->number('AMOUNT', 42)              // Int
    ->decimal('PRICE', 19.99)           // Decimal
    ->date('DUE', Carbon::today())      // date  → YYYY-MM-DD
    ->dateTime('SEEN', Carbon::now())   // datetime → YYYY-MM-DD HH:MM:SS
    ->table('POSITIONS', [              // Table → rows of typed cells
        ['ARTICLE' => 'Widget', 'QTY' => 5],
        ['ARTICLE' => 'Gadget', 'QTY' => 3],
    ]);

DocuWare::documents($cabinetId)->store(
    fileContent: file_get_contents('invoice.pdf'),
    fileName: 'invoice.pdf',
    indexes: $indexes,
);

DocuWare::documents($cabinetId)->update($documentId, IndexFields::make()->number('AMOUNT', 99));
```

Builder methodArgument typeDocuWare `ItemElementName``text($name, $value)``?string``String``memo($name, $value)``?string``Memo``keyword($name, $value)``?string``Keyword``number($name, $value)``?int``Int``decimal($name, $value)``int|float|null``Decimal``date($name, $value)``?Carbon``String` (date)`dateTime($name, $value)``?Carbon``String` (datetime)`table($name, $rows)``array|Collection``Table``IndexFields::make()` starts a builder; `isEmpty()` tells you whether anything was added; and `toCollection()` returns the underlying index objects. `store()` and `update()` also accept that raw `Collection` directly if you need to build the DTOs yourself, but `IndexFields` is the recommended path.

### Updating index fields

[](#updating-index-fields)

`update()` writes index values on an existing document — every field type uses the **same** call, including tables. It returns a `Collection` keyed by field name (the server's view of the fields after the write):

```
use CodebarAg\DocuWare\Data\Write\IndexFields;
use Illuminate\Support\Carbon;

$fields = DocuWare::documents($cabinetId)->update(
    $documentId,
    IndexFields::make()
        ->text('STATUS', 'closed')
        ->number('AMOUNT', 99)
        ->decimal('PRICE', 19.99)
        ->date('DUE', Carbon::today())
        ->dateTime('SEEN', Carbon::now())
        // Table fields are replaced wholesale — pass the full set of rows you want stored:
        ->table('POSITIONS', [
            ['ARTICLE' => 'Widget', 'QTY' => 5],
            ['ARTICLE' => 'Gadget', 'QTY' => 3],
        ]),
    forceUpdate: true, // overwrite read-only/system-managed fields where the cabinet allows it
);

$fields['STATUS']->value; // 'closed'
```

Pass `forceUpdate: false` (the default) to let DocuWare reject writes to protected fields; pass `true` to force them through where the cabinet permits.

### Table fields

[](#table-fields)

A table field holds **rows of typed cells**. The same `table($name, $rows)` builder is used for both create (`store()`) and update (`update()`), and accepts rows in either form:

- **Simple** — an associative `[column => value]` map per row; cell types are auto-detected (`string → String`, `int → Int`, `float → Decimal`, `Carbon → DateTime`):

    ```
    IndexFields::make()->table('POSITIONS', [
        ['ARTICLE' => 'Widget', 'QTY' => 5, 'PRICE' => 19.99],
        ['ARTICLE' => 'Gadget', 'QTY' => 3, 'PRICE' => 4.50],
    ]);
    ```
- **Explicit** — build each cell with an index DTO when you need exact typing that auto-detection can't infer (a date-only cell, a keyword, or a memo):

    ```
    use CodebarAg\DocuWare\DTO\Documents\DocumentIndex\IndexTextDTO;
    use CodebarAg\DocuWare\DTO\Documents\DocumentIndex\IndexNumericDTO;
    use CodebarAg\DocuWare\DTO\Documents\DocumentIndex\IndexDateDTO;

    IndexFields::make()->table('POSITIONS', [
        [
            IndexTextDTO::make('ARTICLE', 'Widget'),
            IndexNumericDTO::make('QTY', 5),
            IndexDateDTO::make('DELIVERED', Carbon::today()),
        ],
    ]);
    ```

> **Tables are written wholesale.** A `store()`/`update()` call **replaces every row** of the field, so always pass the complete set of rows you want stored. To append, read the current rows first (see below) and re-send them along with the new ones.

Reading a table back, the field's `->value` is a `Collection`; each row exposes its cells as a `Collection` keyed by column name (cell values already parsed to native PHP types):

```
$document = DocuWare::documents($cabinetId)->find($documentId);
$rows = $document->fields['POSITIONS']->value;   // Collection

foreach ($rows as $row) {
    $row->fields['ARTICLE']->value;  // 'Widget' (string)
    $row->fields['QTY']->value;      // 5 (int)
}
```

### Reading values back

[](#reading-values-back)

The reverse conversion is automatic. Index values read from DocuWare are exposed on `DocumentFieldData->value` already cast to the matching PHP type:

DocuWare typePHP type on `->value``Int``int``Decimal``float``Date` / `DateTime``Carbon``String` / `Memo` / `Keyword(s)``string``Table``Collection` (each row is a `Collection`)```
$document = DocuWare::documents($cabinetId)->find($documentId);

foreach ($document->fields as $field) {
    $field->name;  // 'AMOUNT'
    $field->value; // 99 (int) — already parsed, no manual conversion
}
```

Immutable data &amp; withers
----------------------------

[](#immutable-data--withers)

Every response is an immutable `spatie/laravel-data` object:

```
$user    = DocuWare::users()->find($id);
$updated = $user->copyWith(active: false);   // a new instance; $user is untouched
$array   = $user->toArray();                 // serialization for free
```

Enums
-----

[](#enums)

EnumCases`TargetFileType``AUTO`, `PDF`, `ORIGINAL``DialogType``SEARCH`, `STORE`, `RESULT`, `INDEX`, `LIST`, `FOLDERS``DocuWareFieldTypeEnum``STRING`, `INT`, `DECIMAL`, `DATE`, `DATETIME`, `TABLE``Grant``Credentials`, `TrustedUser`, `Token`Encrypted document URLs
-----------------------

[](#encrypted-document-urls)

```
$url = DocuWare::url($url, $username, $password, $passphrase)
    ->fileCabinet($cabinetId)
    ->document($documentId)
    ->validUntil($carbon)   // optional expiry
    ->make();
```

Endpoint catalog
----------------

[](#endpoint-catalog)

The resources above are the public API. Under the hood they send Saloon **requests** that map 1:1 to DocuWare Platform REST endpoints. This catalog documents that internal mapping for transparency and advanced use. Paths are relative to `/{platform_path}/` (default `DocuWare/Platform`). **Cached** = the request implements Saloon's `Cacheable` (TTL from `configurations.cache.*`).

**All 68 endpoints across 25 groups**### Authentication › OAuth

[](#authentication--oauth)

RequestMethodEndpointReturnsCachedInputs`GetIdentityServiceConfiguration`GET`{identityServiceUrl}/.well-known/openid-configuration``IdentityServiceConfiguration`yesidentityServiceUrl`GetResponsibleIdentityService`GET`/{platform_path}/Home/IdentityServiceInfo``ResponsibleIdentityService`yesurl`RequestTokenWithCredentials`POST`{tokenEndpoint}``RequestTokenDto`tokenEndpoint, clientId, scope, username, password`RequestTokenWithCredentialsTrustedUser`POST`{tokenEndpoint}``RequestTokenDto`tokenEndpoint, clientId, scope, username, password, impersonateName`RequestTokenWithDocuWareToken`POST`{tokenEndpoint}``RequestTokenDto`tokenEndpoint, token, clientId, scope### Documents

[](#documents)

RequestMethodEndpointReturnsCachedInputs`GetDocumentPreviewRequest`GET`/FileCabinets/{fileCabinetId}/Documents/{documentId}/Image``string`yesfileCabinetId, documentId### Documents › ApplicationProperties

[](#documents--applicationproperties)

RequestMethodEndpointReturnsCachedInputs`AddApplicationProperties`POST`/FileCabinets/{fileCabinetId}/Documents/{documentId}/DocumentApplicationProperties``mixed`yesfileCabinetId, documentId, properties`DeleteApplicationProperties`POST`/FileCabinets/{fileCabinetId}/Documents/{documentId}/DocumentApplicationProperties``mixed`yesfileCabinetId, documentId, propertyNames`GetApplicationProperties`GET`/FileCabinets/{fileCabinetId}/Documents/{documentId}/DocumentApplicationProperties``mixed`yesfileCabinetId, documentId`UpdateApplicationProperties`POST`/FileCabinets/{fileCabinetId}/Documents/{documentId}/DocumentApplicationProperties``mixed`yesfileCabinetId, documentId, properties### Documents › ClipUnclipStapleUnstaple

[](#documents--clipunclipstapleunstaple)

RequestMethodEndpointReturnsCachedInputs`Clip`POST`/FileCabinets/{documentTrayId}/Operations/ContentMerge``Document`documentTrayId, documents, force`Staple`POST`/FileCabinets/{documentTrayId}/Operations/ContentMerge``Document`documentTrayId, documents, force`Unclip`POST`/FileCabinets/{documentTrayId}/Operations/ContentDivide``DocumentPaginator`documentTrayId, documentId`Unstaple`POST`/FileCabinets/{documentTrayId}/Operations/ContentDivide``DocumentPaginator`documentTrayId, documentId### Documents › DocumentsTrashBin

[](#documents--documentstrashbin)

RequestMethodEndpointReturnsCachedInputs`DeleteDocuments`POST`/TrashBin/BatchDelete``DeleteDocumentsDto`ids`GetDocuments`POST`/TrashBin/Query``TrashDocumentPaginator`yespage, perPage, searchTerm, orderField, orderDirection, condition, forceRefresh`RestoreDocuments`POST`/TrashBin/BatchRestore``RestoreDocumentsDto`ids### Documents › Download

[](#documents--download)

RequestMethodEndpointReturnsCachedInputs`DownloadDocument`GET`/FileCabinets/{fileCabinetId}/Documents/{documentId}/FileDownload``mixed`yesfileCabinetId, documentId, targetFileType, keepAnnotations`DownloadSection`GET`/FileCabinets/{fileCabinetId}/Sections/{sectionId}/Data``mixed`yesfileCabinetId, sectionId`DownloadThumbnail`GET`/FileCabinets/{fileCabinetId}/Rendering/{sectionId}/Thumbnail``mixed`yesfileCabinetId, sectionId, page### Documents › ModifyDocuments

[](#documents--modifydocuments)

RequestMethodEndpointReturnsCachedInputs`DeleteDocument`DELETE`/FileCabinets/{fileCabinetId}/Documents/{documentId}``Response`fileCabinetId, documentId`TransferDocument`POST`?``bool`fileCabinetId, destinationFileCabinetId, documentId, storeDialogId, fields, keepSource, fillIntellix, useDefaultDialog### Documents › Sections

[](#documents--sections)

RequestMethodEndpointReturnsCachedInputs`DeleteSection`DELETE`/FileCabinets/{fileCabinetId}/Sections/{sectionId}``bool`yesfileCabinetId, sectionId`GetASpecificSection`GET`/FileCabinets/{fileCabinetId}/Sections/{sectionId}``Section`yesfileCabinetId, sectionId`GetAllSectionsFromADocument`GET`/FileCabinets/{fileCabinetId}/Sections``Collection`yesfileCabinetId, documentId`GetTextshot`GET`/FileCabinets/{fileCabinetId}/Sections/{sectionId}/Textshot``mixed`yesfileCabinetId, sectionId### Documents › Stamps

[](#documents--stamps)

RequestMethodEndpointReturnsCachedInputs`AddDocumentAnnotations`POST`/FileCabinets/{fileCabinetId}/Documents/{documentId}/Annotation``array`fileCabinetId, documentId, payload`GetDocumentAnnotations`GET`/FileCabinets/{fileCabinetId}/Documents/{documentId}/Annotation``Collection`yesfileCabinetId, documentId`GetStamps`GET`/FileCabinets/{fileCabinetId}/Stamps``Collection`fileCabinetId### Documents › UpdateIndexValues

[](#documents--updateindexvalues)

RequestMethodEndpointReturnsCachedInputs`UpdateIndexValues`PUT`/FileCabinets/{fileCabinetId}/Documents/{documentId}/Fields``Collection`fileCabinetId, documentId, indexes, forceUpdate### Fields

[](#fields)

RequestMethodEndpointReturnsCachedInputs`GetFieldsRequest`GET`/FileCabinets/{fileCabinetId}``Collection`yesfileCabinetId### FileCabinets › Batch

[](#filecabinets--batch)

RequestMethodEndpointReturnsCachedInputs`BatchDocumentsUpdateFields`POST`/FileCabinets/{fileCabinetId}/Operations/BatchDocumentsUpdateFields``array`fileCabinetId, payload, contentType### FileCabinets › CheckInCheckOut

[](#filecabinets--checkincheckout)

RequestMethodEndpointReturnsCachedInputs`CheckInDocumentFromFileSystem`POST`/FileCabinets/{fileCabinetId}/Documents/{documentId}/CheckInFromFileSystem``Document`fileCabinetId, documentId, checkInJson, fileContent, fileName`CheckoutDocumentToFileSystem`POST`/FileCabinets/{fileCabinetId}/Documents/{documentId}/CheckoutToFileSystem``CheckoutToFileSystemResult`fileCabinetId, documentId`UndoDocumentCheckout`PUT`/FileCabinets/{fileCabinetId}/Operations/ProcessDocumentAction?DocId={documentId}``Document`fileCabinetId, documentId### FileCabinets › Dialogs

[](#filecabinets--dialogs)

RequestMethodEndpointReturnsCachedInputs`GetASpecificDialog`GET`/FileCabinets/{fileCabinetId}/Dialogs/{dialogId}``mixed`yesfileCabinetId, dialogId`GetAllDialogs`GET`/FileCabinets/{fileCabinetId}/Dialogs``mixed`yesfileCabinetId`GetDialogsOfASpecificType`GET`/FileCabinets/{fileCabinetId}/Dialogs``mixed`yesfileCabinetId, dialogType### FileCabinets › General

[](#filecabinets--general)

RequestMethodEndpointReturnsCachedInputs`GetFileCabinetInformation`GET`/FileCabinets/{fileCabinetId}``FileCabinetInformation`yesfileCabinetId`GetTotalNumberOfDocuments`GET`/FileCabinets/{fileCabinetId}/Query/CountExpression``int`yesfileCabinetId, searchDialogId### FileCabinets › Search

[](#filecabinets--search)

RequestMethodEndpointReturnsCachedInputs`GetASpecificDocumentFromAFileCabinet`GET`/FileCabinets/{fileCabinetId}/Documents/{documentId}``Document`yesfileCabinetId, documentId`GetDocumentsFromAFileCabinet`GET`/FileCabinets/{fileCabinetId}/Documents``DocumentPaginator`yesfileCabinetId, fields, page, perPage### FileCabinets › SelectLists

[](#filecabinets--selectlists)

RequestMethodEndpointReturnsCachedInputs`GetFilteredSelectLists`POST`/FileCabinets/{fileCabinetId}/Query/SelectListExpression``mixed`yesfileCabinetId, dialogId, fieldName, dialogExpression`GetSelectLists`POST`/FileCabinets/{fileCabinetId}/Query/SelectListExpression``mixed`yesfileCabinetId, dialogId, fieldName### FileCabinets › Upload

[](#filecabinets--upload)

RequestMethodEndpointReturnsCachedInputs`AppendASinglePDFToADocument`POST`/FileCabinets/{fileCabinetId}/Sections``Section`fileCabinetId, documentId, fileContent, fileName`AppendFilesToADataRecord`POST`/FileCabinets/{fileCabinetId}/Documents/{dataRecordId}``Document`fileCabinetId, dataRecordId, files`CreateDataRecord`POST`/FileCabinets/{fileCabinetId}/Documents``Document`fileCabinetId, fileContent, fileName, indexes, storeDialogId`ReplaceAPDFDocumentSection`POST`/FileCabinets/{fileCabinetId}/Sections/{sectionId}/Data``Section`fileCabinetId, sectionId, fileContent, fileName### General › Organization

[](#general--organization)

RequestMethodEndpointReturnsCachedInputs`GetAllFileCabinetsAndDocumentTrays`GET`/FileCabinets``Collection`yesorganizationId`GetLoginToken`POST`/Organization/LoginToken``string`yestargetProducts, usage, lifetime`GetOrganization`GET`/Organizations``Collection`yes### General › UserManagement › CreateUpdateUsers

[](#general--usermanagement--createupdateusers)

RequestMethodEndpointReturnsCachedInputs`CreateUser`POST`/Organization/UserInfo``GetUser`yesuser`UpdateUser`POST`/Organization/UserInfo``User`yesuser### General › UserManagement › GetModifyGroups

[](#general--usermanagement--getmodifygroups)

RequestMethodEndpointReturnsCachedInputs`AddUserToAGroup`PUT`/Organization/UserGroups``Response`userId, ids`GetAllGroupsForASpecificUser`GET`/Organization/UserGroups``Collection`yesuserId, name, active`GetGroups`GET`/Organization/Groups``Collection`yesname, active`RemoveUserFromAGroup`PUT`/Organization/UserGroups``Response`userId, ids### General › UserManagement › GetModifyRoles

[](#general--usermanagement--getmodifyroles)

RequestMethodEndpointReturnsCachedInputs`AddUserToARole`PUT`/Organization/UserRoles``Response`userId, ids`GetAllRolesForASpecificUser`GET`/Organization/UserRoles``Collection`yesuserId, name, active, type`GetRoles`GET`/Organization/Roles``Collection`yesname, active, type`RemoveUserFromARole`PUT`/Organization/UserRoles``Response`userId, ids### General › UserManagement › GetUsers

[](#general--usermanagement--getusers)

RequestMethodEndpointReturnsCachedInputs`GetUserById`GET`/Organization/UserByID``User`yesuserId`GetUsers`GET`/Organization/Users``Collection`yesname, active`GetUsersOfAGroup`GET`/Organization/GroupUsers``Collection`yesgroupId`GetUsersOfARole`GET`/Organization/RoleUsers``Collection`yesroleId, includeGroupUsers### Search

[](#search)

RequestMethodEndpointReturnsCachedInputs`GetSearchRequest`POST`/FileCabinets/{fileCabinetId}/Query/DialogExpression``DocumentPaginator`yesfileCabinetId, dialogId, additionalFileCabinetIds, page, perPage, searchTerm, orderField, orderDirection, condition### Workflow

[](#workflow)

RequestMethodEndpointReturnsCachedInputs`GetDocumentWorkflowHistory`GET`/FileCabinets/{fileCabinetId}/Documents/{documentId}/WorkflowHistory``Collection`yesfileCabinetId, documentId`GetDocumentWorkflowHistorySteps`GET`/Workflows/{workflowId}/Instances/{workflowInstanceId}/History``InstanceHistory`yesworkflowId, workflowInstanceIdData object (DTO) reference
---------------------------

[](#data-object-dto-reference)

Responses are mapped into immutable `*Data` objects under `CodebarAg\DocuWare\Data`. The most-used shapes and their properties:

**Document data objects**### `DocumentData` — `Data\Documents\DocumentData`

[](#documentdata--datadocumentsdocumentdata)

PropertyTypeNotes`id``int`Document identifier`title``string`Document title`file_size``int`Bytes`total_pages``int`Page count`extension``?string`File extension`content_type``string`MIME type`file_cabinet_id``string`Cabinet id`created_at` / `updated_at``Carbon`Timestamps`fields``?Collection`Index values keyed by field name`sections``?Collection`Document sections`suggestions``?Collection`Index suggestions`flags``?DocumentFlagsData`Document flags`checksum_info``?ChecksumInfoData`Checksum metadata`version``?DocumentVersionData`Version info`links``?Collection`Hypermedia links### `DocumentFieldData` — `Data\Documents\DocumentFieldData`

[](#documentfielddata--datadocumentsdocumentfielddata)

PropertyTypeNotes`name``string`Field name (e.g. `AMOUNT`)`label``string`Display label`value``null|int|float|Carbon|string|Collection`Parsed to native type (table → `Collection`)`type``string`Wire type (`String`, `Int`, `Decimal`, `Date`, `Table`, …)`isNull``bool`Value is null`systemField``bool`System vs user field`readOnly``?bool`Read-only flag### `DocumentPageData` — `Data\Documents\DocumentPageData`

[](#documentpagedata--datadocumentsdocumentpagedata)

PropertyType`total` / `perPage` / `currentPage` / `lastPage` / `from` / `to``int``documents``Collection`### `FieldData` — `Data\Documents\FieldData`

[](#fielddata--datadocumentsfielddata)

PropertyTypeNotes`name` / `label` / `type` / `scope``string`DB name, display name, field type, System/User`length` / `precision``?int`Size &amp; decimal precision`notEmpty` / `usedAsDocumentName``?bool`Constraints`tableFieldColumns``?array`For table fields### `TableRowData` — `Data\Documents\TableRowData`

[](#tablerowdata--datadocumentstablerowdata)

PropertyType`fields``Collection` (keyed by field name)**File cabinets, dialogs &amp; sections**### `FileCabinetInformationData` — `Data\FileCabinets\FileCabinetInformationData`

[](#filecabinetinformationdata--datafilecabinetsfilecabinetinformationdata)

PropertyTypeNotes`id` / `name` / `color``string`Identity`isBasket` / `usable` / `default``bool`Flags`versionManagement``string`Version management mode`hasFullTextSupport``bool`Full-text search enabled`addIndexEntriesInUpperCase``bool`Upper-cases index entries`fields``?array`Field metadata### `FileCabinetData` — `Data\Organization\FileCabinetData`

[](#filecabinetdata--dataorganizationfilecabinetdata)

Lighter cabinet shape returned by `fileCabinets()->all()` (id, name, color, isBasket, usable, default…).

### `DialogData` — `Data\FileCabinets\DialogData`

[](#dialogdata--datafilecabinetsdialogdata)

PropertyTypeNotes`id` / `type` / `label``string`Identity &amp; dialog type`isDefault``bool`Default dialog`fileCabinetId``string`Owning cabinet`fields``?array`Raw fields; `fieldObjects()` returns typed `DialogFieldData`### `DialogFieldData` — `Data\FileCabinets\DialogFieldData`

[](#dialogfielddata--datafilecabinetsdialogfielddata)

`dbName`, `label`, `type`, `length`, `precision`, `locked`, `readOnly`, `notEmpty`, `visible`, `usedAsDocumentName`, plus select-list metadata.

### `SectionData` — `Data\SectionData`

[](#sectiondata--datasectiondata)

`id`, `contentType`, `pageCount`, `fileSize`, `originalFileName`, `haveMorePages`, `contentModified`, `links`, `pages`, `thumbnails`.

**Organization &amp; user management**### `OrganizationData` — `Data\Organization\OrganizationData`

[](#organizationdata--dataorganizationorganizationdata)

`id`, `name`, `guid`, `additionalInfo`, `configurationRights`, `isTwoStepVerificationEnabled`, `isTwoStepVerificationRequired`, `links`.

### `UserData` — `Data\Users\UserData`

[](#userdata--datausersuserdata)

`id`, `name`, `firstName`, `lastName`, `email`, `dbName`, `active`, `isHighSecurity`, `defaultWebBasket`, `outOfOffice`, `regionalSettings`, `twoStepVerificationEnabled`, `links`.

### `GroupData` — `Data\Users\GroupData`

[](#groupdata--datausersgroupdata)

`id`, `name`, `active`, `links`.

### `RoleData` — `Data\Users\RoleData`

[](#roledata--datausersroledata)

`id`, `name`, `active`, `type`, `links`.

Caching
-------

[](#caching)

`GET` metadata and search requests implement Saloon's `Cacheable` (see the **Cached** column in the endpoint catalog). Cached responses use the `configurations.cache.driver` store with a TTL of `configurations.cache.lifetime_in_seconds`. Set the lifetime to `0` to disable caching. Search requests honor a per-call force-refresh; write operations are never cached.

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

[](#error-handling)

Operations return data or throw — there is no error-carrying result object. Every package exception descends from `DocuWareException`, so you can catch the base type to handle any DocuWare failure, or a specific subclass for a specific status.

ExceptionWhen`AuthenticationException`401`BadRequestException`400`ForbiddenException`403`NotFoundException`404`MethodNotAllowedException`405`ConflictException`409`ValidationException`422`RateLimitException`429`ConnectionException`network/transport failure`RequestException` / `DocuWareException`any other 4xx / 5xxEach exception exposes redacted, structured context for logging and reporting:

MemberDescription`statusCode`HTTP status code`instance`Which DocuWare instance produced it`docuwareMessage`The DocuWare error message`requestId`Correlation id for support`context()`Full redacted context arraySecrets are never present in any exception message or context.

Events
------

[](#events)

```
use CodebarAg\DocuWare\Events\{ResponseReceived, TokenRefreshed};
```

- `ResponseReceived` — a redacted snapshot (instance, method, path, status, request id). The body is attached only when `docuware.debug.capture_bodies` is enabled, and is redacted even then.
- `TokenRefreshed` — instance + url, no secret.

Security
--------

[](#security)

- **Structural redaction**: `Authorization`, passwords, tokens, cookies, and the passphrase are stripped from every log, event, and exception. An architecture test asserts no sentinel secret can leak.
- **Token store**: access tokens are `Crypt`-encrypted, namespaced per instance, and refreshed under a cache lock to avoid stampedes.

Testing
-------

[](#testing)

```
composer test          # offline (fixtures replay), the CI gate
composer test:live     # live integration suite against a real instance
composer analyse       # PHPStan (max)
composer format        # Pint
```

In your own app, fake DocuWare with Saloon's mock client, or assert against the recorded fixtures under `tests/Fixtures/saloon`.

Credits
-------

[](#credits)

- [Sebastian Bürgin-Fix](https://github.com/StanBarrows)
- [codebar Solutions AG](https://www.codebar.ch)
- [Ricoh Schweiz AG](https://www.ricoh.ch)
- [All Contributors](../../contributors)

License
-------

[](#license)

The MIT License (MIT). See [License File](LICENSE.md) for more information.

###  Health Score

61

—

FairBetter than 99% of packages

Maintenance86

Actively maintained with recent releases

Popularity37

Limited adoption so far

Community16

Small or concentrated contributor base

Maturity86

Battle-tested with a long release history

 Bus Factor2

2 contributors hold 50%+ of commits

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 ~25 days

Recently: every ~16 days

Total

76

Last Release

69d ago

Major Versions

v5.2 → v11.02024-07-30

v11.09 → v12.02025-03-03

v11.1.0 → v12.0.12025-03-05

v12.15.0 → v13.3.02026-04-08

v13.6.0 → v14.0.02026-06-11

PHP version history (7 changes)0.0.0PHP ^8.0

1.2.0PHP ^8.1

v2.0PHP ^8.2

v11.0PHP &gt;=8.2

v12.0PHP 8.2.\*|8.3.\*|8.4.\*

v13.3.0PHP 8.3.\*|8.4.\*|8.5.\*

v14.0.0PHP 8.4.\*|8.5.\*

### Community

Maintainers

![](https://www.gravatar.com/avatar/fd1bbe54b79d041b513a286058b6fa0c479343f82147da3e63eefdf29c2fa6cb?d=identicon)[codebar](/maintainers/codebar)

---

Top Contributors

[![RhysLees](https://avatars.githubusercontent.com/u/43909932?v=4)](https://github.com/RhysLees "RhysLees (203 commits)")[![ruslansteiger](https://avatars.githubusercontent.com/u/14092921?v=4)](https://github.com/ruslansteiger "ruslansteiger (139 commits)")[![dependabot[bot]](https://avatars.githubusercontent.com/in/29110?v=4)](https://github.com/dependabot[bot] "dependabot[bot] (30 commits)")[![faissaloux](https://avatars.githubusercontent.com/u/60013703?v=4)](https://github.com/faissaloux "faissaloux (28 commits)")[![StanBarrows](https://avatars.githubusercontent.com/u/10268813?v=4)](https://github.com/StanBarrows "StanBarrows (13 commits)")

---

Tags

docuwarelaravelpackagephplaravelcodebar-agdocuwarelaravel-docuwarecodebar Solutions AGRicoh Schweiz AGDocu Ware

###  Code Quality

TestsPest

Static AnalysisPHPStan, Rector

Code StyleLaravel Pint

### Embed Badge

![Health badge](/badges/codebar-ag-laravel-docuware/health.svg)

```
[![Health](https://phpackages.com/badges/codebar-ag-laravel-docuware/health.svg)](https://phpackages.com/packages/codebar-ag-laravel-docuware)
```

###  Alternatives

[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)[spatie/laravel-health

Monitor the health of a Laravel application

88212.7M189](/packages/spatie-laravel-health)[defstudio/telegraph

A laravel facade to interact with Telegram Bots

818355.4k3](/packages/defstudio-telegraph)[nativephp/mobile

NativePHP for Mobile

1.1k102.1k151](/packages/nativephp-mobile)[codebar-ag/laravel-zammad

Zammad integration with Laravel

107.4k](/packages/codebar-ag-laravel-zammad)

PHPackages © 2026

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