codebar-ag / laravel-docuware
DocuWare integration with Laravel
Requires
- php: 8.4.*|8.5.*
- guzzlehttp/guzzle: ^7.11.1
- illuminate/contracts: ^13.0
- illuminate/support: ^13.0
- nesbot/carbon: ^3.11.4
- saloonphp/cache-plugin: ^3.1
- saloonphp/laravel-plugin: ^4.3.0
- saloonphp/rate-limit-plugin: ^2.0
- saloonphp/saloon: ^4.0
- spatie/laravel-data: ^4.0
- spatie/laravel-package-tools: ^1.93.1
Requires (Dev)
- larastan/larastan: ^3.10.0
- laravel/pint: ^1.29.1
- nunomaduro/collision: ^8.9.4
- orchestra/testbench: ^11.1.0
- pestphp/pest: ^4.7.2
- phpdocumentor/reflection-docblock: ^6.0
- phpstan/extension-installer: ^1.4.3
- phpstan/phpstan-deprecation-rules: ^2.0.4
- phpstan/phpstan-phpunit: ^2.0.16
- rector/rector: ^2.0
- spatie/laravel-ray: ^1.43.9
This package is auto-updated.
Last update: 2026-06-11 14:23:16 UTC
README
An opinionated, modern integration for the DocuWare Platform REST API. 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
- Highlights
- Requirements
- Installation
- Configuration
- Authentication & grants
- Connecting (config file or runtime DTO)
- Quickstart
- Resources
- Search builder
- Index field values
- Immutable data & withers
- Enums
- Encrypted document URLs
- Endpoint catalog
- Data object (DTO) reference
- Caching
- Error handling
- Events
- Security
- Testing
- Credits
- License
Highlights
- 🎯 Small, domain-shaped API —
DocuWare::documents($cabinet)->search()->cursor(). - 🧊 Immutable data via
spatie/laravel-datawith->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 & typed — PHPStan max, offline fixture replay.
Requirements
| Version | PHP | Laravel | DocuWare Cloud |
|---|---|---|---|
| v14 | ^8.4 – ^8.5 | 13.* | ✅ |
| v13 | ^8.3 – ^8.4 | 13.* | ✅ |
| v12 | ^8.2 – ^8.4 | 12.* | ✅ |
| v11 (alpha) | ^8.2 | 11.* | ✅ |
| > v4.0 | ^8.2 | 11.* | ✅ |
| > v3.0 | ^8.2 | 10.* | ✅ |
| > v2.0 | ^8.1 | 9.* | ✅ |
| > v1.2 | ^8.1 | 9.* | ✅ |
| < v1.2 | ^8.0 | 8.* | ✅ |
v14 raises the minimum PHP to 8.4. Laravel 13 stays the target framework.
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
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)
| Key | Env | Default | Purpose |
|---|---|---|---|
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 & 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 & 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 value |
Needs | Use case |
|---|---|---|---|
| Credentials | credentials |
url, username, password | Standard username/password login |
| Trusted user | trusted_user |
url, username, password, impersonate | Authenticate as a trusted user, act as another |
| DocuWare token | token |
url, token | Exchange a DocuWare login token (dwtoken) |
Connecting (config file or runtime DTO)
There are two ways to connect — pick whichever fits your tenancy model.
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)
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
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
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)
The documents resource is broad, so its methods are grouped by area below.
Core (search, read, write, lifecycle)
| Method | Returns | Description |
|---|---|---|
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<DocumentFieldData> |
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)
| Method | Returns | Description |
|---|---|---|
sections($id) / section($sectionId) |
Collection<SectionData> / 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 & application properties
| Method | Returns | Description |
|---|---|---|
annotations($id) / annotate($id, $payload) / stamps($id) |
Collection / mixed / Collection |
Annotations & stamps |
applicationProperties($id) / addApplicationProperties(...) / updateApplicationProperties(...) / deleteApplicationProperties(...) |
mixed |
App-defined properties |
Structure operations (clip/staple, transfer, batch & upload)
| Method | Returns | Description |
|---|---|---|
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 & upload operations |
workflowHistory($id) |
Collection<HistoryStepData> |
Document workflow history |
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<FileCabinetData> DocuWare::fileCabinets()->info($cabinetId); // FileCabinetInformationData DocuWare::fileCabinets()->fields($cabinetId); // Collection<FieldData> — the cabinet's index fields DocuWare::dialogs($cabinetId)->all(); // Collection<DialogData> 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 & users — organizations, users, groups, roles
Tenant-level directory: the organization, its users, and the groups/roles they belong to.
DocuWare::organizations()->all(); // Collection<OrganizationData> DocuWare::organizations()->loginToken($targetProducts, $usage, $lifetime); DocuWare::users()->all(); // Collection<UserData> 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<GroupData> DocuWare::roles()->all(); // Collection<RoleData>
Workflows — workflows
DocuWare::workflows()->historySteps($workflowId, $instanceId); // InstanceHistoryData
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
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);
| Terminal | Returns | Description |
|---|---|---|
get() |
DocumentPageData |
One page of results |
first() |
?DocumentData |
First match, or null |
count() |
int |
Total matching documents |
cursor() |
LazyCollection<DocumentData> |
Memory-safe iteration over all pages |
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 method | Argument type | DocuWare 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
update() writes index values on an existing document — every field type uses the same call,
including tables. It returns a Collection<string, DocumentFieldData> 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
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<TableRowData>; each row exposes its
cells as a Collection<string, DocumentFieldData> keyed by column name (cell values already parsed
to native PHP types):
$document = DocuWare::documents($cabinetId)->find($documentId); $rows = $document->fields['POSITIONS']->value; // Collection<TableRowData> foreach ($rows as $row) { $row->fields['ARTICLE']->value; // 'Widget' (string) $row->fields['QTY']->value; // 5 (int) }
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 type | PHP type on ->value |
|---|---|
Int |
int |
Decimal |
float |
Date / DateTime |
Carbon |
String / Memo / Keyword(s) |
string |
Table |
Collection<TableRowData> (each row is a Collection<string, DocumentFieldData>) |
$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 & 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
| Enum | Cases |
|---|---|
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
$url = DocuWare::url($url, $username, $password, $passphrase) ->fileCabinet($cabinetId) ->document($documentId) ->validUntil($carbon) // optional expiry ->make();
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 <DOCUWARE_URL>/{platform_path}/ (default DocuWare/Platform).
Cached = the request implements Saloon's Cacheable (TTL from configurations.cache.*).
All 68 endpoints across 25 groups
Authentication › OAuth
| Request | Method | Endpoint | Returns | Cached | Inputs |
|---|---|---|---|---|---|
GetIdentityServiceConfiguration |
GET | {identityServiceUrl}/.well-known/openid-configuration |
IdentityServiceConfiguration |
yes | identityServiceUrl |
GetResponsibleIdentityService |
GET | <url>/{platform_path}/Home/IdentityServiceInfo |
ResponsibleIdentityService |
yes | url |
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
| Request | Method | Endpoint | Returns | Cached | Inputs |
|---|---|---|---|---|---|
GetDocumentPreviewRequest |
GET | /FileCabinets/{fileCabinetId}/Documents/{documentId}/Image |
string |
yes | fileCabinetId, documentId |
Documents › ApplicationProperties
| Request | Method | Endpoint | Returns | Cached | Inputs |
|---|---|---|---|---|---|
AddApplicationProperties |
POST | /FileCabinets/{fileCabinetId}/Documents/{documentId}/DocumentApplicationProperties |
mixed |
yes | fileCabinetId, documentId, properties |
DeleteApplicationProperties |
POST | /FileCabinets/{fileCabinetId}/Documents/{documentId}/DocumentApplicationProperties |
mixed |
yes | fileCabinetId, documentId, propertyNames |
GetApplicationProperties |
GET | /FileCabinets/{fileCabinetId}/Documents/{documentId}/DocumentApplicationProperties |
mixed |
yes | fileCabinetId, documentId |
UpdateApplicationProperties |
POST | /FileCabinets/{fileCabinetId}/Documents/{documentId}/DocumentApplicationProperties |
mixed |
yes | fileCabinetId, documentId, properties |
Documents › ClipUnclipStapleUnstaple
| Request | Method | Endpoint | Returns | Cached | Inputs |
|---|---|---|---|---|---|
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
| Request | Method | Endpoint | Returns | Cached | Inputs |
|---|---|---|---|---|---|
DeleteDocuments |
POST | /TrashBin/BatchDelete |
DeleteDocumentsDto |
ids | |
GetDocuments |
POST | /TrashBin/Query |
TrashDocumentPaginator |
yes | page, perPage, searchTerm, orderField, orderDirection, condition, forceRefresh |
RestoreDocuments |
POST | /TrashBin/BatchRestore |
RestoreDocumentsDto |
ids |
Documents › Download
| Request | Method | Endpoint | Returns | Cached | Inputs |
|---|---|---|---|---|---|
DownloadDocument |
GET | /FileCabinets/{fileCabinetId}/Documents/{documentId}/FileDownload |
mixed |
yes | fileCabinetId, documentId, targetFileType, keepAnnotations |
DownloadSection |
GET | /FileCabinets/{fileCabinetId}/Sections/{sectionId}/Data |
mixed |
yes | fileCabinetId, sectionId |
DownloadThumbnail |
GET | /FileCabinets/{fileCabinetId}/Rendering/{sectionId}/Thumbnail |
mixed |
yes | fileCabinetId, sectionId, page |
Documents › ModifyDocuments
| Request | Method | Endpoint | Returns | Cached | Inputs |
|---|---|---|---|---|---|
DeleteDocument |
DELETE | /FileCabinets/{fileCabinetId}/Documents/{documentId} |
Response |
fileCabinetId, documentId | |
TransferDocument |
POST | ? |
bool |
fileCabinetId, destinationFileCabinetId, documentId, storeDialogId, fields, keepSource, fillIntellix, useDefaultDialog |
Documents › Sections
| Request | Method | Endpoint | Returns | Cached | Inputs |
|---|---|---|---|---|---|
DeleteSection |
DELETE | /FileCabinets/{fileCabinetId}/Sections/{sectionId} |
bool |
yes | fileCabinetId, sectionId |
GetASpecificSection |
GET | /FileCabinets/{fileCabinetId}/Sections/{sectionId} |
Section |
yes | fileCabinetId, sectionId |
GetAllSectionsFromADocument |
GET | /FileCabinets/{fileCabinetId}/Sections |
Collection |
yes | fileCabinetId, documentId |
GetTextshot |
GET | /FileCabinets/{fileCabinetId}/Sections/{sectionId}/Textshot |
mixed |
yes | fileCabinetId, sectionId |
Documents › Stamps
| Request | Method | Endpoint | Returns | Cached | Inputs |
|---|---|---|---|---|---|
AddDocumentAnnotations |
POST | /FileCabinets/{fileCabinetId}/Documents/{documentId}/Annotation |
array |
fileCabinetId, documentId, payload | |
GetDocumentAnnotations |
GET | /FileCabinets/{fileCabinetId}/Documents/{documentId}/Annotation |
Collection |
yes | fileCabinetId, documentId |
GetStamps |
GET | /FileCabinets/{fileCabinetId}/Stamps |
Collection |
fileCabinetId |
Documents › UpdateIndexValues
| Request | Method | Endpoint | Returns | Cached | Inputs |
|---|---|---|---|---|---|
UpdateIndexValues |
PUT | /FileCabinets/{fileCabinetId}/Documents/{documentId}/Fields |
Collection |
fileCabinetId, documentId, indexes, forceUpdate |
Fields
| Request | Method | Endpoint | Returns | Cached | Inputs |
|---|---|---|---|---|---|
GetFieldsRequest |
GET | /FileCabinets/{fileCabinetId} |
Collection |
yes | fileCabinetId |
FileCabinets › Batch
| Request | Method | Endpoint | Returns | Cached | Inputs |
|---|---|---|---|---|---|
BatchDocumentsUpdateFields |
POST | /FileCabinets/{fileCabinetId}/Operations/BatchDocumentsUpdateFields |
array |
fileCabinetId, payload, contentType |
FileCabinets › CheckInCheckOut
| Request | Method | Endpoint | Returns | Cached | Inputs |
|---|---|---|---|---|---|
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
| Request | Method | Endpoint | Returns | Cached | Inputs |
|---|---|---|---|---|---|
GetASpecificDialog |
GET | /FileCabinets/{fileCabinetId}/Dialogs/{dialogId} |
mixed |
yes | fileCabinetId, dialogId |
GetAllDialogs |
GET | /FileCabinets/{fileCabinetId}/Dialogs |
mixed |
yes | fileCabinetId |
GetDialogsOfASpecificType |
GET | /FileCabinets/{fileCabinetId}/Dialogs |
mixed |
yes | fileCabinetId, dialogType |
FileCabinets › General
| Request | Method | Endpoint | Returns | Cached | Inputs |
|---|---|---|---|---|---|
GetFileCabinetInformation |
GET | /FileCabinets/{fileCabinetId} |
FileCabinetInformation |
yes | fileCabinetId |
GetTotalNumberOfDocuments |
GET | /FileCabinets/{fileCabinetId}/Query/CountExpression |
int |
yes | fileCabinetId, searchDialogId |
FileCabinets › Search
| Request | Method | Endpoint | Returns | Cached | Inputs |
|---|---|---|---|---|---|
GetASpecificDocumentFromAFileCabinet |
GET | /FileCabinets/{fileCabinetId}/Documents/{documentId} |
Document |
yes | fileCabinetId, documentId |
GetDocumentsFromAFileCabinet |
GET | /FileCabinets/{fileCabinetId}/Documents |
DocumentPaginator |
yes | fileCabinetId, fields, page, perPage |
FileCabinets › SelectLists
| Request | Method | Endpoint | Returns | Cached | Inputs |
|---|---|---|---|---|---|
GetFilteredSelectLists |
POST | /FileCabinets/{fileCabinetId}/Query/SelectListExpression |
mixed |
yes | fileCabinetId, dialogId, fieldName, dialogExpression |
GetSelectLists |
POST | /FileCabinets/{fileCabinetId}/Query/SelectListExpression |
mixed |
yes | fileCabinetId, dialogId, fieldName |
FileCabinets › Upload
| Request | Method | Endpoint | Returns | Cached | Inputs |
|---|---|---|---|---|---|
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
| Request | Method | Endpoint | Returns | Cached | Inputs |
|---|---|---|---|---|---|
GetAllFileCabinetsAndDocumentTrays |
GET | /FileCabinets |
Collection |
yes | organizationId |
GetLoginToken |
POST | /Organization/LoginToken |
string |
yes | targetProducts, usage, lifetime |
GetOrganization |
GET | /Organizations |
Collection |
yes |
General › UserManagement › CreateUpdateUsers
| Request | Method | Endpoint | Returns | Cached | Inputs |
|---|---|---|---|---|---|
CreateUser |
POST | /Organization/UserInfo |
GetUser |
yes | user |
UpdateUser |
POST | /Organization/UserInfo |
User |
yes | user |
General › UserManagement › GetModifyGroups
| Request | Method | Endpoint | Returns | Cached | Inputs |
|---|---|---|---|---|---|
AddUserToAGroup |
PUT | /Organization/UserGroups |
Response |
userId, ids | |
GetAllGroupsForASpecificUser |
GET | /Organization/UserGroups |
Collection |
yes | userId, name, active |
GetGroups |
GET | /Organization/Groups |
Collection |
yes | name, active |
RemoveUserFromAGroup |
PUT | /Organization/UserGroups |
Response |
userId, ids |
General › UserManagement › GetModifyRoles
| Request | Method | Endpoint | Returns | Cached | Inputs |
|---|---|---|---|---|---|
AddUserToARole |
PUT | /Organization/UserRoles |
Response |
userId, ids | |
GetAllRolesForASpecificUser |
GET | /Organization/UserRoles |
Collection |
yes | userId, name, active, type |
GetRoles |
GET | /Organization/Roles |
Collection |
yes | name, active, type |
RemoveUserFromARole |
PUT | /Organization/UserRoles |
Response |
userId, ids |
General › UserManagement › GetUsers
| Request | Method | Endpoint | Returns | Cached | Inputs |
|---|---|---|---|---|---|
GetUserById |
GET | /Organization/UserByID |
User |
yes | userId |
GetUsers |
GET | /Organization/Users |
Collection |
yes | name, active |
GetUsersOfAGroup |
GET | /Organization/GroupUsers |
Collection |
yes | groupId |
GetUsersOfARole |
GET | /Organization/RoleUsers |
Collection |
yes | roleId, includeGroupUsers |
Search
| Request | Method | Endpoint | Returns | Cached | Inputs |
|---|---|---|---|---|---|
GetSearchRequest |
POST | /FileCabinets/{fileCabinetId}/Query/DialogExpression |
DocumentPaginator |
yes | fileCabinetId, dialogId, additionalFileCabinetIds, page, perPage, searchTerm, orderField, orderDirection, condition |
Workflow
| Request | Method | Endpoint | Returns | Cached | Inputs |
|---|---|---|---|---|---|
GetDocumentWorkflowHistory |
GET | /FileCabinets/{fileCabinetId}/Documents/{documentId}/WorkflowHistory |
Collection |
yes | fileCabinetId, documentId |
GetDocumentWorkflowHistorySteps |
GET | /Workflows/{workflowId}/Instances/{workflowInstanceId}/History |
InstanceHistory |
yes | workflowId, workflowInstanceId |
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
| Property | Type | Notes |
|---|---|---|
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<DocumentFieldData> |
Index values keyed by field name |
sections |
?Collection<SectionData> |
Document sections |
suggestions |
?Collection<SuggestionFieldData> |
Index suggestions |
flags |
?DocumentFlagsData |
Document flags |
checksum_info |
?ChecksumInfoData |
Checksum metadata |
version |
?DocumentVersionData |
Version info |
links |
?Collection<LinkData> |
Hypermedia links |
DocumentFieldData — Data\Documents\DocumentFieldData
| Property | Type | Notes |
|---|---|---|
name |
string |
Field name (e.g. AMOUNT) |
label |
string |
Display label |
value |
null|int|float|Carbon|string|Collection |
Parsed to native type (table → Collection<TableRowData>) |
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
| Property | Type |
|---|---|
total / perPage / currentPage / lastPage / from / to |
int |
documents |
Collection<DocumentData> |
FieldData — Data\Documents\FieldData
| Property | Type | Notes |
|---|---|---|
name / label / type / scope |
string |
DB name, display name, field type, System/User |
length / precision |
?int |
Size & decimal precision |
notEmpty / usedAsDocumentName |
?bool |
Constraints |
tableFieldColumns |
?array |
For table fields |
TableRowData — Data\Documents\TableRowData
| Property | Type |
|---|---|
fields |
Collection<string, DocumentFieldData> (keyed by field name) |
File cabinets, dialogs & sections
FileCabinetInformationData — Data\FileCabinets\FileCabinetInformationData
| Property | Type | Notes |
|---|---|---|
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
Lighter cabinet shape returned by fileCabinets()->all() (id, name, color, isBasket, usable, default…).
DialogData — Data\FileCabinets\DialogData
| Property | Type | Notes |
|---|---|---|
id / type / label |
string |
Identity & dialog type |
isDefault |
bool |
Default dialog |
fileCabinetId |
string |
Owning cabinet |
fields |
?array |
Raw fields; fieldObjects() returns typed DialogFieldData |
DialogFieldData — Data\FileCabinets\DialogFieldData
dbName, label, type, length, precision, locked, readOnly, notEmpty, visible,
usedAsDocumentName, plus select-list metadata.
SectionData — Data\SectionData
id, contentType, pageCount, fileSize, originalFileName, haveMorePages, contentModified,
links, pages, thumbnails.
Organization & user management
OrganizationData — Data\Organization\OrganizationData
id, name, guid, additionalInfo, configurationRights, isTwoStepVerificationEnabled,
isTwoStepVerificationRequired, links.
UserData — Data\Users\UserData
id, name, firstName, lastName, email, dbName, active, isHighSecurity,
defaultWebBasket, outOfOffice, regionalSettings, twoStepVerificationEnabled, links.
GroupData — Data\Users\GroupData
id, name, active, links.
RoleData — Data\Users\RoleData
id, name, active, type, links.
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
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.
| Exception | When |
|---|---|
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 / 5xx |
Each exception exposes redacted, structured context for logging and reporting:
| Member | Description |
|---|---|
statusCode |
HTTP status code |
instance |
Which DocuWare instance produced it |
docuwareMessage |
The DocuWare error message |
requestId |
Correlation id for support |
context() |
Full redacted context array |
Secrets are never present in any exception message or context.
Events
use CodebarAg\DocuWare\Events\{ResponseReceived, TokenRefreshed};
ResponseReceived— a redacted snapshot (instance, method, path, status, request id). The body is attached only whendocuware.debug.capture_bodiesis enabled, and is redacted even then.TokenRefreshed— instance + url, no secret.
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
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
License
The MIT License (MIT). See License File for more information.