diff --git a/.fern/metadata.json b/.fern/metadata.json
new file mode 100644
index 0000000..2ffff18
--- /dev/null
+++ b/.fern/metadata.json
@@ -0,0 +1,19 @@
+{
+ "cliVersion": "5.91.0",
+ "generatorName": "fernapi/fern-php-sdk",
+ "generatorVersion": "2.18.1",
+ "generatorConfig": {
+ "namespace": "Cloudpdf",
+ "packageName": "cloudpdf/cloudpdf",
+ "composerJson": {
+ "license": "Apache-2.0",
+ "homepage": "https://www.cloudpdf.com"
+ }
+ },
+ "originGitCommit": "232d5e0240d5673a73c9ea7c7ae9e7b937fdd5b3",
+ "originGitCommitIsDirty": false,
+ "invokedBy": "ci",
+ "requestedVersion": "3.0.0-alpha.1",
+ "ciProvider": "github",
+ "sdkVersion": "3.0.0-alpha.1"
+}
\ No newline at end of file
diff --git a/.github/workflows/sdk-ci.yml b/.github/workflows/sdk-ci.yml
new file mode 100644
index 0000000..a6ffe54
--- /dev/null
+++ b/.github/workflows/sdk-ci.yml
@@ -0,0 +1,22 @@
+name: SDK CI
+
+on:
+ pull_request:
+ push:
+ branches: [main]
+
+permissions:
+ contents: read
+
+jobs:
+ build:
+ name: Build and validate
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v4
+ - uses: shivammathur/setup-php@v2
+ with:
+ php-version: '8.2'
+ tools: composer
+ - run: find src -type f -name '*.php' -print0 | xargs -0 -P 4 -n1 php -l >/dev/null
+ - run: composer validate --no-check-publish --no-interaction
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..31a1aeb
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,5 @@
+.idea
+.php-cs-fixer.cache
+.phpunit.result.cache
+composer.lock
+vendor/
\ No newline at end of file
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
new file mode 100644
index 0000000..0c772cd
--- /dev/null
+++ b/CONTRIBUTING.md
@@ -0,0 +1,120 @@
+# Contributing
+
+Thanks for your interest in contributing to this SDK! This document provides guidelines for contributing to the project.
+
+## Getting Started
+
+### Prerequisites
+
+- PHP 8.1+
+- Composer
+
+### Installation
+
+Install the project dependencies:
+
+```bash
+composer install
+```
+
+### Testing
+
+Run the test suite:
+
+```bash
+composer test
+```
+
+Or run PHPUnit directly:
+
+```bash
+./vendor/bin/phpunit
+```
+
+### Linting & Formatting
+
+Fix code style issues:
+
+```bash
+./vendor/bin/php-cs-fixer fix
+```
+
+### Static Analysis
+
+Run static analysis:
+
+```bash
+./vendor/bin/phpstan analyse
+```
+
+## About Generated Code
+
+**Important**: Most files in this SDK are automatically generated by [Fern](https://buildwithfern.com) from the API definition. Direct modifications to generated files will be overwritten the next time the SDK is generated.
+
+### Generated Files
+
+The following directories contain generated code:
+- `src/` - API client classes and types
+- Most PHP files in the project
+
+### How to Customize
+
+If you need to customize the SDK, you have two options:
+
+#### Option 1: Use `.fernignore`
+
+For custom code that should persist across SDK regenerations:
+
+1. Create a `.fernignore` file in the project root
+2. Add file patterns for files you want to preserve (similar to `.gitignore` syntax)
+3. Add your custom code to those files
+
+Files listed in `.fernignore` will not be overwritten when the SDK is regenerated.
+
+For more information, see the [Fern documentation on custom code](https://buildwithfern.com/learn/sdks/overview/custom-code).
+
+#### Option 2: Contribute to the Generator
+
+If you want to change how code is generated for all users of this SDK:
+
+1. The PHP SDK generator lives in the [Fern repository](https://github.com/fern-api/fern)
+2. Generator code is located at `generators/php/`
+3. Follow the [Fern contributing guidelines](https://github.com/fern-api/fern/blob/main/CONTRIBUTING.md)
+4. Submit a pull request with your changes to the generator
+
+This approach is best for:
+- Bug fixes in generated code
+- New features that would benefit all users
+- Improvements to code generation patterns
+
+## Making Changes
+
+### Workflow
+
+1. Create a new branch for your changes
+2. Make your modifications
+3. Run tests to ensure nothing breaks: `composer test`
+4. Run formatting: `./vendor/bin/php-cs-fixer fix`
+5. Run static analysis: `./vendor/bin/phpstan analyse`
+6. Commit your changes with a clear commit message
+7. Push your branch and create a pull request
+
+### Commit Messages
+
+Write clear, descriptive commit messages that explain what changed and why.
+
+### Code Style
+
+This project uses PHP CS Fixer for automated code formatting. Run `./vendor/bin/php-cs-fixer fix` before committing to ensure your code meets the project's style guidelines.
+
+## Questions or Issues?
+
+If you have questions or run into issues:
+
+1. Check the [Fern documentation](https://buildwithfern.com)
+2. Search existing [GitHub issues](https://github.com/fern-api/fern/issues)
+3. Open a new issue if your question hasn't been addressed
+
+## License
+
+By contributing to this project, you agree that your contributions will be licensed under the same license as the project.
diff --git a/LICENSE b/LICENSE
index 261eeb9..2c656ef 100644
--- a/LICENSE
+++ b/LICENSE
@@ -1,3 +1,19 @@
+Copyright 2026 CloudPDF LTD
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use files in this distribution except in compliance with the License.
+You may obtain a copy of the License at
+
+ https://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+
+-----------------------------------------------------------------------
+
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
@@ -174,28 +190,3 @@
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
-
- APPENDIX: How to apply the Apache License to your work.
-
- To apply the Apache License to your work, attach the following
- boilerplate notice, with the fields enclosed by brackets "[]"
- replaced with your own identifying information. (Don't include
- the brackets!) The text should be enclosed in the appropriate
- comment syntax for the file format. We also recommend that a
- file or class name and description of purpose be included on the
- same "printed page" as the copyright notice for easier
- identification within third-party archives.
-
- Copyright [yyyy] [name of copyright owner]
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
diff --git a/README.md b/README.md
index d31e83d..ec3b22b 100644
--- a/README.md
+++ b/README.md
@@ -1 +1,150 @@
-# cloudpdf-sdk-php
\ No newline at end of file
+# Cloudpdf PHP Library
+
+[](https://buildwithfern.com?utm_source=github&utm_medium=github&utm_campaign=readme&utm_source=Cloudpdf%2FPHP)
+[](https://packagist.org/packages/cloudpdf/cloudpdf)
+
+The Cloudpdf PHP library provides convenient access to the Cloudpdf APIs from PHP.
+
+## Table of Contents
+
+- [Requirements](#requirements)
+- [Installation](#installation)
+- [Usage](#usage)
+- [Exception Handling](#exception-handling)
+- [Advanced](#advanced)
+ - [Custom Client](#custom-client)
+ - [Retries](#retries)
+ - [Timeouts](#timeouts)
+- [Contributing](#contributing)
+
+## Requirements
+
+This SDK requires PHP ^8.1.
+
+## Installation
+
+```sh
+composer require cloudpdf/cloudpdf
+```
+
+## Usage
+
+Instantiate and use the client with the following:
+
+```php
+',
+);
+$client->tenants->create(
+ new TenantsCreateRequest([
+ 'id' => 'id',
+ ]),
+);
+
+```
+
+## Exception Handling
+
+When the API returns a non-success status code (4xx or 5xx response), an exception will be thrown.
+
+```php
+use Cloudpdf\Exceptions\CloudpdfApiException;
+use Cloudpdf\Exceptions\CloudpdfException;
+
+try {
+ $response = $client->tenants->create(...);
+} catch (CloudpdfApiException $e) {
+ echo 'API Exception occurred: ' . $e->getMessage() . "\n";
+ echo 'Status Code: ' . $e->getCode() . "\n";
+ echo 'Response Body: ' . $e->getBody() . "\n";
+ // Optionally, rethrow the exception or handle accordingly.
+}
+```
+
+## Advanced
+
+### Custom Client
+
+This SDK is built to work with any HTTP client that implements the [PSR-18](https://www.php-fig.org/psr/psr-18/) `ClientInterface`.
+By default, if no client is provided, the SDK will use `php-http/discovery` to find an installed HTTP client.
+However, you can pass your own client that adheres to `ClientInterface`:
+
+```php
+use Cloudpdf\CloudpdfClient;
+
+// Pass any PSR-18 compatible HTTP client implementation.
+// For example, using Guzzle:
+$customClient = new \GuzzleHttp\Client([
+ 'timeout' => 5.0,
+]);
+
+$client = new CloudpdfClient(options: [
+ 'client' => $customClient
+]);
+
+// Or using Symfony HttpClient:
+// $customClient = (new \Symfony\Component\HttpClient\Psr18Client())
+// ->withOptions(['timeout' => 5.0]);
+//
+// $client = new CloudpdfClient(options: [
+// 'client' => $customClient
+// ]);
+```
+
+### Retries
+
+The SDK is instrumented with automatic retries with exponential backoff. A request will be retried as long
+as the request is deemed retryable and the number of retry attempts has not grown larger than the configured
+retry limit (default: 2).
+
+A request is deemed retryable when any of the following HTTP status codes is returned:
+
+- [408](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/408) (Timeout)
+- [429](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/429) (Too Many Requests)
+- [5XX](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status#server_error_responses) (Internal Server Error)
+
+The `retryStatusCodes` configuration controls which [5XX](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status#server_error_responses) status codes are retried:
+
+- `legacy` (default): Retries `408`, `429`, and all `>= 500`
+- `recommended`: Retries `408`, `429`, `502`, `503`, `504` only (excludes `500 Internal Server Error` to avoid retrying non-idempotent failures)
+
+Use the `maxRetries` request option to configure this behavior.
+
+```php
+$response = $client->tenants->create(
+ ...,
+ options: [
+ 'maxRetries' => 0 // Override maxRetries at the request level
+ ]
+);
+```
+
+### Timeouts
+
+The SDK defaults to a 30 second timeout. Use the `timeout` option to configure this behavior.
+
+```php
+$response = $client->tenants->create(
+ ...,
+ options: [
+ 'timeout' => 3.0 // Override timeout at the request level
+ ]
+);
+```
+
+## Contributing
+
+While we value open-source contributions to this SDK, this library is generated programmatically.
+Additions made directly to this library would have to be moved over to our generation code,
+otherwise they would be overwritten upon the next generated release. Feel free to open a PR as
+a proof of concept, but know that we will not be able to merge it as-is. We suggest opening
+an issue first to discuss with us!
+
+On the other hand, contributions to the README are always very welcome!
diff --git a/cloudpdf-generation.json b/cloudpdf-generation.json
new file mode 100644
index 0000000..6d84c9f
--- /dev/null
+++ b/cloudpdf-generation.json
@@ -0,0 +1,17 @@
+{
+ "language": "php",
+ "canonicalVersion": "3.0.0-next.1",
+ "sdkVersion": "3.0.0-alpha.1",
+ "source": {
+ "repository": "embedpdf/embed-pdf-viewer",
+ "openapi": "cloudpdf/contract/openapi.json",
+ "openapiSha256": "6858ce7912e9063d8fb171ade95fc473db01c5971a3af241a612dda79cc05100",
+ "gitCommit": "232d5e0240d5673a73c9ea7c7ae9e7b937fdd5b3",
+ "gitCommitIsDirty": false
+ },
+ "fern": {
+ "cliVersion": "5.91.0",
+ "generatorName": "fernapi/fern-php-sdk",
+ "generatorVersion": "2.18.1"
+ }
+}
diff --git a/composer.json b/composer.json
new file mode 100644
index 0000000..82b4ebf
--- /dev/null
+++ b/composer.json
@@ -0,0 +1,47 @@
+{
+ "name": "cloudpdf/cloudpdf",
+ "version": "3.0.0-alpha.1",
+ "description": "Cloudpdf PHP Library",
+ "keywords": [
+ "cloudpdf",
+ "api",
+ "sdk"
+ ],
+ "license": "Apache-2.0",
+ "require": {
+ "php": "^8.1",
+ "ext-json": "*",
+ "psr/http-client": "^1.0",
+ "psr/http-client-implementation": "^1.0",
+ "psr/http-factory": "^1.0",
+ "psr/http-factory-implementation": "^1.0",
+ "psr/http-message": "^1.1 || ^2.0",
+ "php-http/discovery": "^1.0",
+ "php-http/multipart-stream-builder": "^1.0"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^12.5.22",
+ "friendsofphp/php-cs-fixer": "3.5.0",
+ "phpstan/phpstan": "^1.12",
+ "guzzlehttp/guzzle": "^7.4"
+ },
+ "autoload": {
+ "psr-4": {
+ "Cloudpdf\\": "src/"
+ }
+ },
+ "autoload-dev": {
+ "psr-4": {
+ "Cloudpdf\\Tests\\": "tests/"
+ }
+ },
+ "scripts": {
+ "build": [
+ "@php -l src",
+ "@php -l tests"
+ ],
+ "test": "phpunit",
+ "analyze": "phpstan analyze src tests --memory-limit=1G"
+ },
+ "homepage": "https://www.cloudpdf.com"
+}
\ No newline at end of file
diff --git a/phpstan.neon b/phpstan.neon
new file mode 100644
index 0000000..780706b
--- /dev/null
+++ b/phpstan.neon
@@ -0,0 +1,6 @@
+parameters:
+ level: max
+ reportUnmatchedIgnoredErrors: false
+ paths:
+ - src
+ - tests
\ No newline at end of file
diff --git a/phpunit.xml b/phpunit.xml
new file mode 100644
index 0000000..54630a5
--- /dev/null
+++ b/phpunit.xml
@@ -0,0 +1,7 @@
+
+
+
+ tests
+
+
+
\ No newline at end of file
diff --git a/reference.md b/reference.md
new file mode 100644
index 0000000..913c0b0
--- /dev/null
+++ b/reference.md
@@ -0,0 +1,2300 @@
+# Reference
+## Deployment
+$client->deployment->licenseStatus() -> ?DeploymentLicenseStatusResponse
+
+-
+
+#### 🔌 Usage
+
+
+-
+
+
+-
+
+```php
+$client->deployment->licenseStatus();
+```
+
+
+
+
+
+
+
+
+
+
+## Doc
+$client->doc->head($docId, $request) -> ?DocHead200Response
+
+-
+
+#### 🔌 Usage
+
+
+-
+
+
+-
+
+```php
+$client->doc->head(
+ 'docId',
+ new HeadDocRequest([]),
+);
+```
+
+
+
+
+
+#### ⚙️ Parameters
+
+
+-
+
+
+-
+
+**$docId:** `string`
+
+
+
+
+
+-
+
+**$documentPassword:** `?string` — Base64-encoded password for an encrypted document. Valid only with the API token (403 anywhere else). An encrypted document answers 422 DocPasswordRequired when the header is absent. Viewer doc JWTs use the SDK password-session flow instead.
+
+
+
+
+
+
+
+
+
+
+
+$client->doc->download($docId, $layerName, $request) -> string
+
+-
+
+#### 🔌 Usage
+
+
+-
+
+
+-
+
+```php
+$client->doc->download(
+ 'docId',
+ 'layerName',
+ new DownloadDocRequest([]),
+);
+```
+
+
+
+
+
+#### ⚙️ Parameters
+
+
+-
+
+
+-
+
+**$docId:** `string`
+
+
+
+
+
+-
+
+**$layerName:** `string`
+
+
+
+
+
+-
+
+**$documentPassword:** `?string` — Base64-encoded password for an encrypted document. Valid only with the API token (403 anywhere else). An encrypted document answers 422 DocPasswordRequired when the header is absent. Viewer doc JWTs use the SDK password-session flow instead.
+
+
+
+
+
+
+
+
+
+
+
+$client->doc->manifest($docId, $layerName, $request) -> ?DocManifest200Response
+
+-
+
+#### 🔌 Usage
+
+
+-
+
+
+-
+
+```php
+$client->doc->manifest(
+ 'docId',
+ 'layerName',
+ new ManifestDocRequest([]),
+);
+```
+
+
+
+
+
+#### ⚙️ Parameters
+
+
+-
+
+
+-
+
+**$docId:** `string`
+
+
+
+
+
+-
+
+**$layerName:** `string`
+
+
+
+
+
+-
+
+**$documentPassword:** `?string` — Base64-encoded password for an encrypted document. Valid only with the API token (403 anywhere else). An encrypted document answers 422 DocPasswordRequired when the header is absent. Viewer doc JWTs use the SDK password-session flow instead.
+
+
+
+
+
+
+
+
+
+
+
+$client->doc->render($docId, $layerName, $pon, $request) -> string
+
+-
+
+#### 📝 Description
+
+
+-
+
+
+-
+
+Render parameters (viewport, format) pass as flat dotted query keys, e.g. `?viewport.kind=width&viewport.width=800`; the full grammar is documented with the viewer.
+
+
+
+
+
+#### 🔌 Usage
+
+
+-
+
+
+-
+
+```php
+$client->doc->render(
+ 'docId',
+ 'layerName',
+ 1,
+ new RenderDocRequest([]),
+);
+```
+
+
+
+
+
+#### ⚙️ Parameters
+
+
+-
+
+
+-
+
+**$docId:** `string`
+
+
+
+
+
+-
+
+**$layerName:** `string`
+
+
+
+
+
+-
+
+**$pon:** `int`
+
+
+
+
+
+-
+
+**$documentPassword:** `?string` — Base64-encoded password for an encrypted document. Valid only with the API token (403 anywhere else). An encrypted document answers 422 DocPasswordRequired when the header is absent. Viewer doc JWTs use the SDK password-session flow instead.
+
+
+
+
+
+
+
+
+
+
+
+$client->doc->text($docId, $layerName, $pon, $request) -> ?DocText200Response
+
+-
+
+#### 🔌 Usage
+
+
+-
+
+
+-
+
+```php
+$client->doc->text(
+ 'docId',
+ 'layerName',
+ 1,
+ new TextDocRequest([]),
+);
+```
+
+
+
+
+
+#### ⚙️ Parameters
+
+
+-
+
+
+-
+
+**$docId:** `string`
+
+
+
+
+
+-
+
+**$layerName:** `string`
+
+
+
+
+
+-
+
+**$pon:** `int`
+
+
+
+
+
+-
+
+**$documentPassword:** `?string` — Base64-encoded password for an encrypted document. Valid only with the API token (403 anywhere else). An encrypted document answers 422 DocPasswordRequired when the header is absent. Viewer doc JWTs use the SDK password-session flow instead.
+
+
+
+
+
+
+
+
+
+
+
+## Tenants
+$client->tenants->list($request) -> ?TenantsList200Response
+
+-
+
+#### 🔌 Usage
+
+
+-
+
+
+-
+
+```php
+$client->tenants->list(
+ new ListTenantsRequest([]),
+);
+```
+
+
+
+
+
+#### ⚙️ Parameters
+
+
+-
+
+
+-
+
+**$limit:** `?int`
+
+
+
+
+
+-
+
+**$cursor:** `?string`
+
+
+
+
+
+
+
+
+
+
+
+$client->tenants->create($request) -> ?TenantsCreate200Response
+
+-
+
+#### 🔌 Usage
+
+
+-
+
+
+-
+
+```php
+$client->tenants->create(
+ new TenantsCreateRequest([
+ 'id' => 'id',
+ ]),
+);
+```
+
+
+
+
+
+#### ⚙️ Parameters
+
+
+-
+
+
+-
+
+**$id:** `string`
+
+
+
+
+
+-
+
+**$name:** `?string`
+
+
+
+
+
+
+
+
+
+
+
+$client->tenants->get($tenantId) -> ?TenantsGet200Response
+
+-
+
+#### 🔌 Usage
+
+
+-
+
+
+-
+
+```php
+$client->tenants->get(
+ 'tenantId',
+);
+```
+
+
+
+
+
+#### ⚙️ Parameters
+
+
+-
+
+
+-
+
+**$tenantId:** `string`
+
+
+
+
+
+
+
+
+
+
+
+$client->tenants->delete($tenantId)
+
+-
+
+#### 📝 Description
+
+
+-
+
+
+-
+
+Destroys the tenant and everything in its namespace — documents, layers, stored bytes, audit history. Irreversible.
+
+
+
+
+
+#### 🔌 Usage
+
+
+-
+
+
+-
+
+```php
+$client->tenants->delete(
+ 'tenantId',
+);
+```
+
+
+
+
+
+#### ⚙️ Parameters
+
+
+-
+
+
+-
+
+**$tenantId:** `string`
+
+
+
+
+
+
+
+
+
+
+
+## Documents
+$client->documents->list($tenantId, $request) -> ?DocumentsList200Response
+
+-
+
+#### 🔌 Usage
+
+
+-
+
+
+-
+
+```php
+$client->documents->list(
+ 'tenantId',
+ new ListDocumentsRequest([]),
+);
+```
+
+
+
+
+
+#### ⚙️ Parameters
+
+
+-
+
+
+-
+
+**$tenantId:** `string`
+
+
+
+
+
+-
+
+**$limit:** `?int`
+
+
+
+
+
+-
+
+**$cursor:** `?string`
+
+
+
+
+
+-
+
+**$state:** `?string`
+
+
+
+
+
+
+
+
+
+
+
+$client->documents->get($tenantId, $id) -> ?DocumentsGet200Response
+
+-
+
+#### 🔌 Usage
+
+
+-
+
+
+-
+
+```php
+$client->documents->get(
+ 'tenantId',
+ 'id',
+);
+```
+
+
+
+
+
+#### ⚙️ Parameters
+
+
+-
+
+
+-
+
+**$tenantId:** `string`
+
+
+
+
+
+-
+
+**$id:** `string`
+
+
+
+
+
+
+
+
+
+
+
+$client->documents->delete($tenantId, $id)
+
+-
+
+#### 🔌 Usage
+
+
+-
+
+
+-
+
+```php
+$client->documents->delete(
+ 'tenantId',
+ 'id',
+);
+```
+
+
+
+
+
+#### ⚙️ Parameters
+
+
+-
+
+
+-
+
+**$tenantId:** `string`
+
+
+
+
+
+-
+
+**$id:** `string`
+
+
+
+
+
+
+
+
+
+
+
+$client->documents->commit($tenantId, $id, $request) -> ?DocumentsCommit200Response
+
+-
+
+#### 🔌 Usage
+
+
+-
+
+
+-
+
+```php
+$client->documents->commit(
+ 'tenantId',
+ 'id',
+ new DocumentsCommitRequest([
+ 'sha256' => 'sha256',
+ ]),
+);
+```
+
+
+
+
+
+#### ⚙️ Parameters
+
+
+-
+
+
+-
+
+**$tenantId:** `string`
+
+
+
+
+
+-
+
+**$id:** `string`
+
+
+
+
+
+-
+
+**$sha256:** `string`
+
+
+
+
+
+
+
+
+
+
+
+$client->documents->download($tenantId, $id) -> string
+
+-
+
+#### 🔌 Usage
+
+
+-
+
+
+-
+
+```php
+$client->documents->download(
+ 'tenantId',
+ 'id',
+);
+```
+
+
+
+
+
+#### ⚙️ Parameters
+
+
+-
+
+
+-
+
+**$tenantId:** `string`
+
+
+
+
+
+-
+
+**$id:** `string`
+
+
+
+
+
+
+
+
+
+
+
+$client->documents->thumbnail($tenantId, $id) -> string
+
+-
+
+#### 🔌 Usage
+
+
+-
+
+
+-
+
+```php
+$client->documents->thumbnail(
+ 'tenantId',
+ 'id',
+);
+```
+
+
+
+
+
+#### ⚙️ Parameters
+
+
+-
+
+
+-
+
+**$tenantId:** `string`
+
+
+
+
+
+-
+
+**$id:** `string`
+
+
+
+
+
+
+
+
+
+
+
+$client->documents->uploadDirect($tenantId, $id) -> ?DocumentsUploadDirect200Response
+
+-
+
+#### 🔌 Usage
+
+
+-
+
+
+-
+
+```php
+$client->documents->uploadDirect($tenantId, $id): ?DocumentsUploadDirect200Response;
+```
+
+
+
+
+
+#### ⚙️ Parameters
+
+
+-
+
+
+-
+
+**$tenantId:** `string`
+
+
+
+
+
+-
+
+**$id:** `string`
+
+
+
+
+
+
+
+
+
+
+
+$client->documents->init($tenantId, $request) -> ?DocumentsInit200Response
+
+-
+
+#### 🔌 Usage
+
+
+-
+
+
+-
+
+```php
+$client->documents->init(
+ 'tenantId',
+ new DocumentsInitRequest([
+ 'contentLength' => 1.1,
+ 'contentSha256' => 'contentSha256',
+ ]),
+);
+```
+
+
+
+
+
+#### ⚙️ Parameters
+
+
+-
+
+
+-
+
+**$tenantId:** `string`
+
+
+
+
+
+-
+
+**$contentLength:** `float`
+
+
+
+
+
+-
+
+**$contentSha256:** `string`
+
+
+
+
+
+-
+
+**$metadata:** `?array`
+
+
+
+
+
+-
+
+**$idempotencyKey:** `?string`
+
+
+
+
+
+-
+
+**$dedupMode:** `?string`
+
+
+
+
+
+-
+
+**$docId:** `?string`
+
+
+
+
+
+-
+
+**$uploadTtlSec:** `?float`
+
+
+
+
+
+
+
+
+
+
+
+## Tokens
+$client->tokens->issue($tenantId, $request) -> ?TokensIssue200Response
+
+-
+
+#### 📝 Description
+
+
+-
+
+
+-
+
+kind "tenant" requires the API token — authority mints only downward. Mounted only when the deployment can sign (HS256 mode); asymmetric deployments mint with their own private key.
+
+
+
+
+
+#### 🔌 Usage
+
+
+-
+
+
+-
+
+```php
+$client->tokens->issue(
+ 'tenantId',
+ new IssueTokensRequest([
+ 'body' => TokensIssueRequest::doc(new TokensIssueRequestDoc([
+ 'sub' => 'sub',
+ 'docId' => 'docId',
+ 'scope' => [
+ 'scope',
+ ],
+ 'expiresIn' => 1,
+ ])),
+ ]),
+);
+```
+
+
+
+
+
+#### ⚙️ Parameters
+
+
+-
+
+
+-
+
+**$tenantId:** `string`
+
+
+
+
+
+-
+
+**$request:** `TokensIssueRequest`
+
+
+
+
+
+
+
+
+
+
+
+$client->tokens->revoke($tenantId, $jti, $request)
+
+-
+
+#### 📝 Description
+
+
+-
+
+
+-
+
+Mounted only when the deployment enables token revocation.
+
+
+
+
+
+#### 🔌 Usage
+
+
+-
+
+
+-
+
+```php
+$client->tokens->revoke(
+ 'tenantId',
+ 'jti',
+ new TokensRevokeRequest([]),
+);
+```
+
+
+
+
+
+#### ⚙️ Parameters
+
+
+-
+
+
+-
+
+**$tenantId:** `string`
+
+
+
+
+
+-
+
+**$jti:** `string`
+
+
+
+
+
+-
+
+**$reason:** `?string`
+
+
+
+
+
+-
+
+**$expiresAtSeconds:** `?int`
+
+
+
+
+
+
+
+
+
+
+
+## Doc Annotations
+$client->doc->annotations->list($docId, $layerName, $pon, $request) -> ?DocAnnotationsList200Response
+
+-
+
+#### 🔌 Usage
+
+
+-
+
+
+-
+
+```php
+$client->doc->annotations->list(
+ 'docId',
+ 'layerName',
+ 1,
+ new ListAnnotationsRequest([]),
+);
+```
+
+
+
+
+
+#### ⚙️ Parameters
+
+
+-
+
+
+-
+
+**$docId:** `string`
+
+
+
+
+
+-
+
+**$layerName:** `string`
+
+
+
+
+
+-
+
+**$pon:** `int`
+
+
+
+
+
+-
+
+**$documentPassword:** `?string` — Base64-encoded password for an encrypted document. Valid only with the API token (403 anywhere else). An encrypted document answers 422 DocPasswordRequired when the header is absent. Viewer doc JWTs use the SDK password-session flow instead.
+
+
+
+
+
+
+
+
+
+
+
+$client->doc->annotations->create($docId, $layerName, $pon, $request) -> ?DocAnnotationsCreate200Response
+
+-
+
+#### 📝 Description
+
+
+-
+
+
+-
+
+Doc JWTs may instead carry collab scopes (annotations:create:self, …) that refine per-annotation authorship rules; the API token is exempt from both.
+
+
+
+
+
+#### 🔌 Usage
+
+
+-
+
+
+-
+
+```php
+$client->doc->annotations->create(
+ 'docId',
+ 'layerName',
+ 1,
+ new CreateAnnotationsRequest([
+ 'body' => [
+ 'key' => "value",
+ ],
+ ]),
+);
+```
+
+
+
+
+
+#### ⚙️ Parameters
+
+
+-
+
+
+-
+
+**$docId:** `string`
+
+
+
+
+
+-
+
+**$layerName:** `string`
+
+
+
+
+
+-
+
+**$pon:** `int`
+
+
+
+
+
+-
+
+**$documentPassword:** `?string` — Base64-encoded password for an encrypted document. Valid only with the API token (403 anywhere else). An encrypted document answers 422 DocPasswordRequired when the header is absent. Viewer doc JWTs use the SDK password-session flow instead.
+
+
+
+
+
+-
+
+**$request:** `array`
+
+
+
+
+
+
+
+
+
+
+
+$client->doc->annotations->delete($docId, $layerName, $pon, $annotKey, $request) -> ?DocAnnotationsDelete200Response
+
+-
+
+#### 🔌 Usage
+
+
+-
+
+
+-
+
+```php
+$client->doc->annotations->delete(
+ 'docId',
+ 'layerName',
+ 1,
+ 'annotKey',
+ new DeleteAnnotationsRequest([]),
+);
+```
+
+
+
+
+
+#### ⚙️ Parameters
+
+
+-
+
+
+-
+
+**$docId:** `string`
+
+
+
+
+
+-
+
+**$layerName:** `string`
+
+
+
+
+
+-
+
+**$pon:** `int`
+
+
+
+
+
+-
+
+**$annotKey:** `string`
+
+
+
+
+
+-
+
+**$documentPassword:** `?string` — Base64-encoded password for an encrypted document. Valid only with the API token (403 anywhere else). An encrypted document answers 422 DocPasswordRequired when the header is absent. Viewer doc JWTs use the SDK password-session flow instead.
+
+
+
+
+
+
+
+
+
+
+
+$client->doc->annotations->update($docId, $layerName, $pon, $annotKey, $request) -> ?DocAnnotationsUpdate200Response
+
+-
+
+#### 🔌 Usage
+
+
+-
+
+
+-
+
+```php
+$client->doc->annotations->update(
+ 'docId',
+ 'layerName',
+ 1,
+ 'annotKey',
+ new UpdateAnnotationsRequest([
+ 'body' => [
+ 'key' => "value",
+ ],
+ ]),
+);
+```
+
+
+
+
+
+#### ⚙️ Parameters
+
+
+-
+
+
+-
+
+**$docId:** `string`
+
+
+
+
+
+-
+
+**$layerName:** `string`
+
+
+
+
+
+-
+
+**$pon:** `int`
+
+
+
+
+
+-
+
+**$annotKey:** `string`
+
+
+
+
+
+-
+
+**$documentPassword:** `?string` — Base64-encoded password for an encrypted document. Valid only with the API token (403 anywhere else). An encrypted document answers 422 DocPasswordRequired when the header is absent. Viewer doc JWTs use the SDK password-session flow instead.
+
+
+
+
+
+-
+
+**$request:** `array`
+
+
+
+
+
+
+
+
+
+
+
+## Doc Forms
+$client->doc->forms->get($docId, $layerName, $request) -> ?DocFormsGet200Response
+
+-
+
+#### 🔌 Usage
+
+
+-
+
+
+-
+
+```php
+$client->doc->forms->get(
+ 'docId',
+ 'layerName',
+ new GetFormsRequest([]),
+);
+```
+
+
+
+
+
+#### ⚙️ Parameters
+
+
+-
+
+
+-
+
+**$docId:** `string`
+
+
+
+
+
+-
+
+**$layerName:** `string`
+
+
+
+
+
+-
+
+**$documentPassword:** `?string` — Base64-encoded password for an encrypted document. Valid only with the API token (403 anywhere else). An encrypted document answers 422 DocPasswordRequired when the header is absent. Viewer doc JWTs use the SDK password-session flow instead.
+
+
+
+
+
+
+
+
+
+
+
+$client->doc->forms->exportData($docId, $layerName, $request) -> string
+
+-
+
+#### 🔌 Usage
+
+
+-
+
+
+-
+
+```php
+$client->doc->forms->exportData(
+ 'docId',
+ 'layerName',
+ new ExportDataFormsRequest([]),
+);
+```
+
+
+
+
+
+#### ⚙️ Parameters
+
+
+-
+
+
+-
+
+**$docId:** `string`
+
+
+
+
+
+-
+
+**$layerName:** `string`
+
+
+
+
+
+-
+
+**$format:** `?string`
+
+
+
+
+
+-
+
+**$documentPassword:** `?string` — Base64-encoded password for an encrypted document. Valid only with the API token (403 anywhere else). An encrypted document answers 422 DocPasswordRequired when the header is absent. Viewer doc JWTs use the SDK password-session flow instead.
+
+
+
+
+
+
+
+
+
+
+
+$client->doc->forms->importData($docId, $layerName, $request) -> ?DocFormsImportData200Response
+
+-
+
+#### 🔌 Usage
+
+
+-
+
+
+-
+
+```php
+$client->doc->forms->importData(
+ 'docId',
+ 'layerName',
+ new ImportDataFormsRequest([
+ 'body' => [
+ 'key' => "value",
+ ],
+ ]),
+);
+```
+
+
+
+
+
+#### ⚙️ Parameters
+
+
+-
+
+
+-
+
+**$docId:** `string`
+
+
+
+
+
+-
+
+**$layerName:** `string`
+
+
+
+
+
+-
+
+**$documentPassword:** `?string` — Base64-encoded password for an encrypted document. Valid only with the API token (403 anywhere else). An encrypted document answers 422 DocPasswordRequired when the header is absent. Viewer doc JWTs use the SDK password-session flow instead.
+
+
+
+
+
+-
+
+**$request:** `array`
+
+
+
+
+
+
+
+
+
+
+
+$client->doc->forms->reset($docId, $layerName, $fieldKey, $request) -> ?DocFormsReset200Response
+
+-
+
+#### 🔌 Usage
+
+
+-
+
+
+-
+
+```php
+$client->doc->forms->reset(
+ 'docId',
+ 'layerName',
+ 'fieldKey',
+ new ResetFormsRequest([]),
+);
+```
+
+
+
+
+
+#### ⚙️ Parameters
+
+
+-
+
+
+-
+
+**$docId:** `string`
+
+
+
+
+
+-
+
+**$layerName:** `string`
+
+
+
+
+
+-
+
+**$fieldKey:** `string`
+
+
+
+
+
+-
+
+**$documentPassword:** `?string` — Base64-encoded password for an encrypted document. Valid only with the API token (403 anywhere else). An encrypted document answers 422 DocPasswordRequired when the header is absent. Viewer doc JWTs use the SDK password-session flow instead.
+
+
+
+
+
+
+
+
+
+
+
+$client->doc->forms->setValue($docId, $layerName, $fieldKey, $request) -> ?DocFormsSetValue200Response
+
+-
+
+#### 🔌 Usage
+
+
+-
+
+
+-
+
+```php
+$client->doc->forms->setValue(
+ 'docId',
+ 'layerName',
+ 'fieldKey',
+ new SetValueFormsRequest([
+ 'body' => [
+ 'key' => "value",
+ ],
+ ]),
+);
+```
+
+
+
+
+
+#### ⚙️ Parameters
+
+
+-
+
+
+-
+
+**$docId:** `string`
+
+
+
+
+
+-
+
+**$layerName:** `string`
+
+
+
+
+
+-
+
+**$fieldKey:** `string`
+
+
+
+
+
+-
+
+**$documentPassword:** `?string` — Base64-encoded password for an encrypted document. Valid only with the API token (403 anywhere else). An encrypted document answers 422 DocPasswordRequired when the header is absent. Viewer doc JWTs use the SDK password-session flow instead.
+
+
+
+
+
+-
+
+**$request:** `array`
+
+
+
+
+
+
+
+
+
+
+
+## Doc Metadata
+$client->doc->metadata->get($docId, $layerName, $request) -> ?DocMetadataGet200Response
+
+-
+
+#### 🔌 Usage
+
+
+-
+
+
+-
+
+```php
+$client->doc->metadata->get(
+ 'docId',
+ 'layerName',
+ new GetMetadataRequest([]),
+);
+```
+
+
+
+
+
+#### ⚙️ Parameters
+
+
+-
+
+
+-
+
+**$docId:** `string`
+
+
+
+
+
+-
+
+**$layerName:** `string`
+
+
+
+
+
+-
+
+**$documentPassword:** `?string` — Base64-encoded password for an encrypted document. Valid only with the API token (403 anywhere else). An encrypted document answers 422 DocPasswordRequired when the header is absent. Viewer doc JWTs use the SDK password-session flow instead.
+
+
+
+
+
+
+
+
+
+
+
+## Doc Pages
+$client->doc->pages->delete($docId, $layerName, $request) -> ?DocPagesDelete200Response
+
+-
+
+#### 🔌 Usage
+
+
+-
+
+
+-
+
+```php
+$client->doc->pages->delete(
+ 'docId',
+ 'layerName',
+ new DeletePagesRequest([
+ 'body' => [
+ 'key' => "value",
+ ],
+ ]),
+);
+```
+
+
+
+
+
+#### ⚙️ Parameters
+
+
+-
+
+
+-
+
+**$docId:** `string`
+
+
+
+
+
+-
+
+**$layerName:** `string`
+
+
+
+
+
+-
+
+**$documentPassword:** `?string` — Base64-encoded password for an encrypted document. Valid only with the API token (403 anywhere else). An encrypted document answers 422 DocPasswordRequired when the header is absent. Viewer doc JWTs use the SDK password-session flow instead.
+
+
+
+
+
+-
+
+**$request:** `array`
+
+
+
+
+
+
+
+
+
+
+
+$client->doc->pages->flatten($docId, $layerName, $request) -> ?DocPagesFlatten200Response
+
+-
+
+#### 🔌 Usage
+
+
+-
+
+
+-
+
+```php
+$client->doc->pages->flatten(
+ 'docId',
+ 'layerName',
+ new FlattenPagesRequest([
+ 'body' => [
+ 'key' => "value",
+ ],
+ ]),
+);
+```
+
+
+
+
+
+#### ⚙️ Parameters
+
+
+-
+
+
+-
+
+**$docId:** `string`
+
+
+
+
+
+-
+
+**$layerName:** `string`
+
+
+
+
+
+-
+
+**$documentPassword:** `?string` — Base64-encoded password for an encrypted document. Valid only with the API token (403 anywhere else). An encrypted document answers 422 DocPasswordRequired when the header is absent. Viewer doc JWTs use the SDK password-session flow instead.
+
+
+
+
+
+-
+
+**$request:** `array`
+
+
+
+
+
+
+
+
+
+
+
+$client->doc->pages->move($docId, $layerName, $request) -> ?DocPagesMove200Response
+
+-
+
+#### 🔌 Usage
+
+
+-
+
+
+-
+
+```php
+$client->doc->pages->move(
+ 'docId',
+ 'layerName',
+ new MovePagesRequest([
+ 'body' => [
+ 'key' => "value",
+ ],
+ ]),
+);
+```
+
+
+
+
+
+#### ⚙️ Parameters
+
+
+-
+
+
+-
+
+**$docId:** `string`
+
+
+
+
+
+-
+
+**$layerName:** `string`
+
+
+
+
+
+-
+
+**$documentPassword:** `?string` — Base64-encoded password for an encrypted document. Valid only with the API token (403 anywhere else). An encrypted document answers 422 DocPasswordRequired when the header is absent. Viewer doc JWTs use the SDK password-session flow instead.
+
+
+
+
+
+-
+
+**$request:** `array`
+
+
+
+
+
+
+
+
+
+
+
+$client->doc->pages->rotate($docId, $layerName, $request) -> ?DocPagesRotate200Response
+
+-
+
+#### 🔌 Usage
+
+
+-
+
+
+-
+
+```php
+$client->doc->pages->rotate(
+ 'docId',
+ 'layerName',
+ new RotatePagesRequest([
+ 'body' => [
+ 'key' => "value",
+ ],
+ ]),
+);
+```
+
+
+
+
+
+#### ⚙️ Parameters
+
+
+-
+
+
+-
+
+**$docId:** `string`
+
+
+
+
+
+-
+
+**$layerName:** `string`
+
+
+
+
+
+-
+
+**$documentPassword:** `?string` — Base64-encoded password for an encrypted document. Valid only with the API token (403 anywhere else). An encrypted document answers 422 DocPasswordRequired when the header is absent. Viewer doc JWTs use the SDK password-session flow instead.
+
+
+
+
+
+-
+
+**$request:** `array`
+
+
+
+
+
+
+
+
+
+
+
+## Doc Redactions
+$client->doc->redactions->apply($docId, $layerName, $request) -> ?DocRedactionsApply200Response
+
+-
+
+#### 🔌 Usage
+
+
+-
+
+
+-
+
+```php
+$client->doc->redactions->apply(
+ 'docId',
+ 'layerName',
+ new ApplyRedactionsRequest([
+ 'body' => [
+ 'key' => "value",
+ ],
+ ]),
+);
+```
+
+
+
+
+
+#### ⚙️ Parameters
+
+
+-
+
+
+-
+
+**$docId:** `string`
+
+
+
+
+
+-
+
+**$layerName:** `string`
+
+
+
+
+
+-
+
+**$documentPassword:** `?string` — Base64-encoded password for an encrypted document. Valid only with the API token (403 anywhere else). An encrypted document answers 422 DocPasswordRequired when the header is absent. Viewer doc JWTs use the SDK password-session flow instead.
+
+
+
+
+
+-
+
+**$request:** `array`
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/CloudpdfClient.php b/src/CloudpdfClient.php
new file mode 100644
index 0000000..3383b6e
--- /dev/null
+++ b/src/CloudpdfClient.php
@@ -0,0 +1,95 @@
+,
+ * } $options @phpstan-ignore-next-line Property is used in endpoint methods via HttpEndpointGenerator
+ */
+ private array $options;
+
+ /**
+ * @var RawClient $client
+ */
+ private RawClient $client;
+
+ /**
+ * @param string $token The token to use for authentication.
+ * @param ?array{
+ * baseUrl?: string,
+ * client?: ClientInterface,
+ * maxRetries?: int,
+ * timeout?: float,
+ * headers?: array,
+ * } $options
+ */
+ public function __construct(
+ string $token,
+ ?array $options = null,
+ ) {
+ $defaultHeaders = [
+ 'Authorization' => "Bearer $token",
+ 'X-Fern-Language' => 'PHP',
+ 'X-Fern-SDK-Name' => 'Cloudpdf',
+ 'X-Fern-SDK-Version' => '3.0.0-alpha.1',
+ 'User-Agent' => 'cloudpdf/cloudpdf/3.0.0-alpha.1',
+ ];
+
+ $this->options = $options ?? [];
+
+ $this->options['headers'] = array_merge(
+ $defaultHeaders,
+ $this->options['headers'] ?? [],
+ );
+
+ $this->client = new RawClient(
+ options: $this->options,
+ );
+
+ $this->deployment = new DeploymentClient($this->client, $this->options);
+ $this->doc = new DocClient($this->client, $this->options);
+ $this->tenants = new TenantsClient($this->client, $this->options);
+ $this->documents = new DocumentsClient($this->client, $this->options);
+ $this->tokens = new TokensClient($this->client, $this->options);
+ }
+}
diff --git a/src/Core/Client/BaseApiRequest.php b/src/Core/Client/BaseApiRequest.php
new file mode 100644
index 0000000..76c0315
--- /dev/null
+++ b/src/Core/Client/BaseApiRequest.php
@@ -0,0 +1,22 @@
+ $headers Additional headers for the request (optional)
+ * @param array $query Query parameters for the request (optional)
+ */
+ public function __construct(
+ public readonly string $baseUrl,
+ public readonly string $path,
+ public readonly HttpMethod $method,
+ public readonly array $headers = [],
+ public readonly array $query = [],
+ ) {
+ }
+}
diff --git a/src/Core/Client/HttpClientBuilder.php b/src/Core/Client/HttpClientBuilder.php
new file mode 100644
index 0000000..a611545
--- /dev/null
+++ b/src/Core/Client/HttpClientBuilder.php
@@ -0,0 +1,56 @@
+
+ */
+ private array $responses = [];
+
+ /**
+ * @var array
+ */
+ private array $requests = [];
+
+ /**
+ * @param ResponseInterface ...$responses
+ */
+ public function append(ResponseInterface ...$responses): void
+ {
+ foreach ($responses as $response) {
+ $this->responses[] = $response;
+ }
+ }
+
+ /**
+ * @param RequestInterface $request
+ * @return ResponseInterface
+ */
+ public function sendRequest(RequestInterface $request): ResponseInterface
+ {
+ $this->requests[] = $request;
+
+ if (empty($this->responses)) {
+ throw new RuntimeException('No more responses in the queue. Add responses using append().');
+ }
+
+ return array_shift($this->responses);
+ }
+
+ /**
+ * @return ?RequestInterface
+ */
+ public function getLastRequest(): ?RequestInterface
+ {
+ if (empty($this->requests)) {
+ return null;
+ }
+ return $this->requests[count($this->requests) - 1];
+ }
+
+ /**
+ * @return int
+ */
+ public function getRequestCount(): int
+ {
+ return count($this->requests);
+ }
+
+ /**
+ * Returns the number of remaining responses in the queue.
+ *
+ * @return int
+ */
+ public function count(): int
+ {
+ return count($this->responses);
+ }
+}
diff --git a/src/Core/Client/RawClient.php b/src/Core/Client/RawClient.php
new file mode 100644
index 0000000..c078601
--- /dev/null
+++ b/src/Core/Client/RawClient.php
@@ -0,0 +1,359 @@
+ $headers
+ */
+ private array $headers;
+
+ /**
+ * @var ?(callable(): array) $getAuthHeaders
+ */
+ private $getAuthHeaders;
+
+ /**
+ * @param ?array{
+ * baseUrl?: string,
+ * client?: ClientInterface,
+ * maxRetries?: int,
+ * timeout?: float,
+ * headers?: array,
+ * getAuthHeaders?: callable(): array,
+ * } $options
+ */
+ public function __construct(
+ public readonly ?array $options = null,
+ ) {
+ $this->client = HttpClientBuilder::build(
+ $this->options['client'] ?? null,
+ $this->options['maxRetries'] ?? 2,
+ );
+ $this->requestFactory = HttpClientBuilder::requestFactory();
+ $this->streamFactory = HttpClientBuilder::streamFactory();
+ $this->headers = $this->options['headers'] ?? [];
+ $this->getAuthHeaders = $this->options['getAuthHeaders'] ?? null;
+ }
+
+ /**
+ * @param BaseApiRequest $request
+ * @param ?array{
+ * maxRetries?: int,
+ * timeout?: float,
+ * headers?: array,
+ * queryParameters?: array,
+ * bodyProperties?: array,
+ * } $options
+ * @return ResponseInterface
+ * @throws ClientExceptionInterface
+ */
+ public function sendRequest(
+ BaseApiRequest $request,
+ ?array $options = null,
+ ): ResponseInterface {
+ $opts = $options ?? [];
+ $httpRequest = $this->buildRequest($request, $opts);
+
+ $timeout = $opts['timeout'] ?? $this->options['timeout'] ?? null;
+ $maxRetries = $opts['maxRetries'] ?? null;
+
+ return $this->client->send($httpRequest, $timeout, $maxRetries);
+ }
+
+ /**
+ * @param BaseApiRequest $request
+ * @param array{
+ * headers?: array,
+ * queryParameters?: array,
+ * bodyProperties?: array,
+ * } $options
+ * @return RequestInterface
+ */
+ private function buildRequest(
+ BaseApiRequest $request,
+ array $options
+ ): RequestInterface {
+ $url = $this->buildUrl($request, $options);
+ $headers = $this->encodeHeaders($request, $options);
+
+ $httpRequest = $this->requestFactory->createRequest(
+ $request->method->name,
+ $url,
+ );
+
+ // Encode body and, for multipart, capture the Content-Type with boundary.
+ if ($request instanceof MultipartApiRequest && $request->body !== null) {
+ $builder = new MultipartStreamBuilder($this->streamFactory);
+ $request->body->addToBuilder($builder);
+ $httpRequest = $httpRequest->withBody($builder->build());
+ $headers['Content-Type'] = "multipart/form-data; boundary={$builder->getBoundary()}";
+ } else {
+ $body = $this->encodeRequestBody($request, $options);
+ if ($body !== null) {
+ $httpRequest = $httpRequest->withBody($body);
+ }
+ }
+
+ foreach ($headers as $name => $value) {
+ $httpRequest = $httpRequest->withHeader($name, $value);
+ }
+
+ return $httpRequest;
+ }
+
+ /**
+ * @param BaseApiRequest $request
+ * @param array{
+ * headers?: array,
+ * } $options
+ * @return array
+ */
+ private function encodeHeaders(
+ BaseApiRequest $request,
+ array $options,
+ ): array {
+ $authHeaders = $this->getAuthHeaders !== null ? ($this->getAuthHeaders)() : [];
+ return match (get_class($request)) {
+ JsonApiRequest::class => array_merge(
+ [
+ "Content-Type" => "application/json",
+ "Accept" => "*/*",
+ ],
+ $this->headers,
+ $authHeaders,
+ $request->headers,
+ $options['headers'] ?? [],
+ ),
+ UrlEncodedApiRequest::class => array_merge(
+ [
+ "Content-Type" => "application/x-www-form-urlencoded",
+ "Accept" => "*/*",
+ ],
+ $this->headers,
+ $authHeaders,
+ $request->headers,
+ $options['headers'] ?? [],
+ ),
+ MultipartApiRequest::class => array_merge(
+ $this->headers,
+ $authHeaders,
+ $request->headers,
+ $options['headers'] ?? [],
+ ),
+ default => throw new InvalidArgumentException('Unsupported request type: ' . get_class($request)),
+ };
+ }
+
+ /**
+ * @param BaseApiRequest $request
+ * @param array{
+ * bodyProperties?: array,
+ * } $options
+ * @return ?StreamInterface
+ */
+ private function encodeRequestBody(
+ BaseApiRequest $request,
+ array $options,
+ ): ?StreamInterface {
+ if ($request instanceof JsonApiRequest) {
+ return $request->body === null ? null : $this->streamFactory->createStream(
+ JsonEncoder::encode(
+ $this->buildJsonBody(
+ $request->body,
+ $options,
+ ),
+ )
+ );
+ }
+
+ if ($request instanceof UrlEncodedApiRequest) {
+ if ($request->body === null) {
+ return null;
+ }
+ $body = $this->buildJsonBody($request->body, $options);
+ if ($body instanceof JsonSerializable) {
+ $body = $body->jsonSerialize();
+ }
+ if (is_object($body)) {
+ $body = (array)$body;
+ }
+ if (!is_array($body)) {
+ throw new InvalidArgumentException('URL-encoded request bodies must serialize to an array.');
+ }
+ return $this->streamFactory->createStream(http_build_query($body));
+ }
+
+ if ($request instanceof MultipartApiRequest) {
+ return null;
+ }
+
+ throw new InvalidArgumentException('Unsupported request type: ' . get_class($request));
+ }
+
+ /**
+ * @param mixed $body
+ * @param array{
+ * bodyProperties?: array,
+ * } $options
+ * @return mixed
+ */
+ private function buildJsonBody(
+ mixed $body,
+ array $options,
+ ): mixed {
+ $overrideProperties = $options['bodyProperties'] ?? [];
+ if (is_array($body) && (empty($body) || self::isSequential($body))) {
+ return array_merge($body, $overrideProperties);
+ }
+
+ if ($body instanceof JsonSerializable) {
+ $result = $body->jsonSerialize();
+ } else {
+ $result = $body;
+ }
+ if (is_array($result)) {
+ $result = array_merge($result, $overrideProperties);
+ if (empty($result)) {
+ // force to be serialized as {} instead of []
+ return (object)($result);
+ }
+ }
+
+ return $result;
+ }
+
+ /**
+ * Percent-encodes a value for use inside a single URL path segment. Encoding happens here,
+ * where the value is still separate from the path template, so that a value containing "/"
+ * or ".." cannot change which endpoint the request resolves to.
+ */
+ public static function encodePathParam(mixed $value): string
+ {
+ if (is_bool($value)) {
+ return $value ? 'true' : 'false';
+ }
+ if (is_null($value)) {
+ return '';
+ }
+ if (is_scalar($value)) {
+ return rawurlencode((string)$value);
+ }
+ if (is_object($value) && method_exists($value, '__toString')) {
+ return rawurlencode((string)$value);
+ }
+ return rawurlencode(JsonEncoder::encode($value));
+ }
+
+ /**
+ * @param BaseApiRequest $request
+ * @param array{
+ * queryParameters?: array,
+ * } $options
+ * @return string
+ */
+ private function buildUrl(
+ BaseApiRequest $request,
+ array $options,
+ ): string {
+ $baseUrl = $request->baseUrl;
+ $trimmedBaseUrl = rtrim($baseUrl, '/');
+ $trimmedBasePath = ltrim($request->path, '/');
+ $url = "{$trimmedBaseUrl}/{$trimmedBasePath}";
+ $query = array_merge(
+ $request->query,
+ $options['queryParameters'] ?? [],
+ );
+ if (!empty($query)) {
+ $url .= '?' . $this->encodeQuery($query);
+ }
+ return $url;
+ }
+
+ /**
+ * @param array $query
+ * @return string
+ */
+ private function encodeQuery(array $query): string
+ {
+ $parts = [];
+ foreach ($query as $key => $value) {
+ if (is_array($value)) {
+ foreach ($value as $item) {
+ $parts[] = urlencode($key) . '=' . $this->encodeQueryValue($item);
+ }
+ } else {
+ $parts[] = urlencode($key) . '=' . $this->encodeQueryValue($value);
+ }
+ }
+ return implode('&', $parts);
+ }
+
+ private function encodeQueryValue(mixed $value): string
+ {
+ if (is_string($value)) {
+ return urlencode($value);
+ }
+ if (is_bool($value)) {
+ return $value ? 'true' : 'false';
+ }
+ if (is_scalar($value)) {
+ return urlencode((string)$value);
+ }
+ if (is_null($value)) {
+ return 'null';
+ }
+ // Unreachable, but included for a best effort.
+ return urlencode(JsonEncoder::encode($value));
+ }
+
+ /**
+ * Check if an array is sequential, not associative.
+ * @param mixed[] $arr
+ * @return bool
+ */
+ private static function isSequential(array $arr): bool
+ {
+ if (empty($arr)) {
+ return false;
+ }
+ $length = count($arr);
+ $keys = array_keys($arr);
+ for ($i = 0; $i < $length; $i++) {
+ if ($keys[$i] !== $i) {
+ return false;
+ }
+ }
+ return true;
+ }
+}
diff --git a/src/Core/Client/RetryDecoratingClient.php b/src/Core/Client/RetryDecoratingClient.php
new file mode 100644
index 0000000..0bdbc5b
--- /dev/null
+++ b/src/Core/Client/RetryDecoratingClient.php
@@ -0,0 +1,241 @@
+client = $client;
+ $this->maxRetries = $maxRetries;
+ $this->baseDelay = $baseDelay;
+ $this->sleepFunction = $sleepFunction ?? 'usleep';
+ }
+
+ /**
+ * @param RequestInterface $request
+ * @return ResponseInterface
+ * @throws ClientExceptionInterface
+ */
+ public function sendRequest(RequestInterface $request): ResponseInterface
+ {
+ return $this->send($request);
+ }
+
+ /**
+ * Sends a request with optional per-request timeout and retry overrides.
+ *
+ * When a Guzzle or Symfony PSR-18 client is detected, the timeout is
+ * forwarded via the client's native API. For other PSR-18 clients the
+ * timeout value is silently ignored.
+ *
+ * @param RequestInterface $request
+ * @param ?float $timeout Timeout in seconds, or null to use the client default.
+ * @param ?int $maxRetries Maximum retry attempts, or null to use the client default.
+ * @return ResponseInterface
+ * @throws ClientExceptionInterface
+ */
+ public function send(
+ RequestInterface $request,
+ ?float $timeout = null,
+ ?int $maxRetries = null,
+ ): ResponseInterface {
+ $maxRetries = $maxRetries ?? $this->maxRetries;
+ $retryAttempt = 0;
+ $lastResponse = null;
+
+ while (true) {
+ try {
+ $lastResponse = $this->doSend($request, $timeout);
+ if (!$this->shouldRetry($retryAttempt, $maxRetries, $lastResponse)) {
+ return $lastResponse;
+ }
+ } catch (ClientExceptionInterface $e) {
+ if ($retryAttempt >= $maxRetries) {
+ throw $e;
+ }
+ }
+
+ $retryAttempt++;
+ $delay = $this->getRetryDelay($retryAttempt, $lastResponse);
+ ($this->sleepFunction)($delay * 1000); // Convert milliseconds to microseconds
+
+ // Rewind the request body so retries don't send an empty body.
+ $request->getBody()->rewind();
+ }
+ }
+
+ /**
+ * Dispatches the request to the underlying client, forwarding the timeout
+ * option to Guzzle or Symfony when available.
+ *
+ * @param RequestInterface $request
+ * @param ?float $timeout
+ * @return ResponseInterface
+ * @throws ClientExceptionInterface
+ */
+ private function doSend(RequestInterface $request, ?float $timeout): ResponseInterface
+ {
+ static $warned = false;
+
+ if ($timeout === null) {
+ return $this->client->sendRequest($request);
+ }
+
+ if (interface_exists('GuzzleHttp\ClientInterface')
+ && $this->client instanceof \GuzzleHttp\ClientInterface
+ ) {
+ return $this->client->send($request, ['timeout' => $timeout]);
+ }
+ if (class_exists('Symfony\Component\HttpClient\Psr18Client')
+ && $this->client instanceof \Symfony\Component\HttpClient\Psr18Client
+ ) {
+ /** @var ClientInterface $clientWithTimeout */
+ $clientWithTimeout = $this->client->withOptions(['timeout' => $timeout]);
+ return $clientWithTimeout->sendRequest($request);
+ }
+
+ if ($warned) {
+ return $this->client->sendRequest($request);
+ }
+ $warned = true;
+ trigger_error(
+ 'Timeout option is not supported for the current PSR-18 client ('
+ . get_class($this->client)
+ . '). Use Guzzle or Symfony HttpClient for timeout support.',
+ E_USER_WARNING,
+ );
+ return $this->client->sendRequest($request);
+ }
+
+ /**
+ * @param int $retryAttempt
+ * @param int $maxRetries
+ * @param ?ResponseInterface $response
+ * @return bool
+ */
+ private function shouldRetry(
+ int $retryAttempt,
+ int $maxRetries,
+ ?ResponseInterface $response = null,
+ ): bool {
+ if ($retryAttempt >= $maxRetries) {
+ return false;
+ }
+
+ if ($response !== null) {
+ return $response->getStatusCode() >= 500 ||
+ in_array($response->getStatusCode(), self::RETRY_STATUS_CODES);
+ }
+
+ return false;
+ }
+
+ /**
+ * Calculate the retry delay based on response headers or exponential backoff.
+ *
+ * @param int $retryAttempt
+ * @param ?ResponseInterface $response
+ * @return int milliseconds
+ */
+ private function getRetryDelay(int $retryAttempt, ?ResponseInterface $response): int
+ {
+ if ($response !== null) {
+ // Check Retry-After header
+ $retryAfter = $response->getHeaderLine('Retry-After');
+ if ($retryAfter !== '') {
+ // Try parsing as integer (seconds)
+ if (is_numeric($retryAfter)) {
+ $retryAfterSeconds = (int)$retryAfter;
+ if ($retryAfterSeconds > 0) {
+ return min($retryAfterSeconds * 1000, self::MAX_RETRY_DELAY);
+ }
+ }
+
+ // Try parsing as HTTP date
+ $retryAfterDate = strtotime($retryAfter);
+ if ($retryAfterDate !== false) {
+ $delay = ($retryAfterDate - time()) * 1000;
+ if ($delay > 0) {
+ return min(max($delay, 0), self::MAX_RETRY_DELAY);
+ }
+ }
+ }
+
+ // Check X-RateLimit-Reset header
+ $rateLimitReset = $response->getHeaderLine('X-RateLimit-Reset');
+ if ($rateLimitReset !== '' && is_numeric($rateLimitReset)) {
+ $resetTime = (int)$rateLimitReset;
+ $delay = ($resetTime * 1000) - (int)(microtime(true) * 1000);
+ if ($delay > 0) {
+ return $this->addPositiveJitter(min($delay, self::MAX_RETRY_DELAY));
+ }
+ }
+ }
+
+ // Fall back to exponential backoff with symmetric jitter
+ return $this->addSymmetricJitter(
+ min($this->exponentialDelay($retryAttempt), self::MAX_RETRY_DELAY)
+ );
+ }
+
+ /**
+ * Add positive jitter (0% to +20%) to the delay.
+ *
+ * @param int $delay
+ * @return int
+ */
+ private function addPositiveJitter(int $delay): int
+ {
+ $jitterMultiplier = 1 + (mt_rand() / mt_getrandmax()) * self::JITTER_FACTOR;
+ return (int)($delay * $jitterMultiplier);
+ }
+
+ /**
+ * Add symmetric jitter (-10% to +10%) to the delay.
+ *
+ * @param int $delay
+ * @return int
+ */
+ private function addSymmetricJitter(int $delay): int
+ {
+ $jitterMultiplier = 1 + ((mt_rand() / mt_getrandmax()) - 0.5) * self::JITTER_FACTOR;
+ return (int)($delay * $jitterMultiplier);
+ }
+
+ /**
+ * Default exponential backoff delay function.
+ *
+ * @return int milliseconds.
+ */
+ private function exponentialDelay(int $retryAttempt): int
+ {
+ return 2 ** ($retryAttempt - 1) * $this->baseDelay;
+ }
+}
diff --git a/src/Core/Client/UrlEncodedApiRequest.php b/src/Core/Client/UrlEncodedApiRequest.php
new file mode 100644
index 0000000..d29a93b
--- /dev/null
+++ b/src/Core/Client/UrlEncodedApiRequest.php
@@ -0,0 +1,25 @@
+ $headers Additional headers for the request (optional)
+ * @param array $query Query parameters for the request (optional)
+ * @param mixed|null $body The form-urlencoded request body (optional)
+ */
+ public function __construct(
+ string $baseUrl,
+ string $path,
+ HttpMethod $method,
+ array $headers = [],
+ array $query = [],
+ public readonly mixed $body = null
+ ) {
+ parent::__construct($baseUrl, $path, $method, $headers, $query);
+ }
+}
diff --git a/src/Core/Json/JsonApiRequest.php b/src/Core/Json/JsonApiRequest.php
new file mode 100644
index 0000000..4488de2
--- /dev/null
+++ b/src/Core/Json/JsonApiRequest.php
@@ -0,0 +1,28 @@
+ $headers Additional headers for the request (optional)
+ * @param array $query Query parameters for the request (optional)
+ * @param mixed|null $body The JSON request body (optional)
+ */
+ public function __construct(
+ string $baseUrl,
+ string $path,
+ HttpMethod $method,
+ array $headers = [],
+ array $query = [],
+ public readonly mixed $body = null
+ ) {
+ parent::__construct($baseUrl, $path, $method, $headers, $query);
+ }
+}
diff --git a/src/Core/Json/JsonDecoder.php b/src/Core/Json/JsonDecoder.php
new file mode 100644
index 0000000..d291871
--- /dev/null
+++ b/src/Core/Json/JsonDecoder.php
@@ -0,0 +1,161 @@
+ $type The type definition for deserialization.
+ * @return mixed[]|array The deserialized array.
+ * @throws JsonException If the decoded value is not an array.
+ */
+ public static function decodeArray(string $json, array $type): array
+ {
+ $decoded = self::decode($json);
+ if (!is_array($decoded)) {
+ throw new JsonException("Unexpected non-array json value: $json");
+ }
+ return JsonDeserializer::deserializeArray($decoded, $type);
+ }
+
+ /**
+ * Decodes a JSON string and deserializes it based on the provided union type definition.
+ *
+ * @param string $json The JSON string to decode.
+ * @param Union $union The union type definition for deserialization.
+ * @return mixed The deserialized value.
+ * @throws JsonException If the deserialization for all types in the union fails.
+ */
+ public static function decodeUnion(string $json, Union $union): mixed
+ {
+ $decoded = self::decode($json);
+ return JsonDeserializer::deserializeUnion($decoded, $union);
+ }
+ /**
+ * Decodes a JSON string and returns a mixed.
+ *
+ * @param string $json The JSON string to decode.
+ * @return mixed The decoded mixed.
+ * @throws JsonException If the decoded value is not an mixed.
+ */
+ public static function decodeMixed(string $json): mixed
+ {
+ return self::decode($json);
+ }
+
+ /**
+ * Decodes a JSON string into a PHP value.
+ *
+ * @param string $json The JSON string to decode.
+ * @return mixed The decoded value.
+ * @throws JsonException If an error occurs during JSON decoding.
+ */
+ public static function decode(string $json): mixed
+ {
+ return json_decode($json, associative: true, flags: JSON_THROW_ON_ERROR);
+ }
+}
diff --git a/src/Core/Json/JsonDeserializer.php b/src/Core/Json/JsonDeserializer.php
new file mode 100644
index 0000000..2340f85
--- /dev/null
+++ b/src/Core/Json/JsonDeserializer.php
@@ -0,0 +1,218 @@
+ $data The array to be deserialized.
+ * @param array $type The type definition from the annotation.
+ * @return array The deserialized array.
+ * @throws JsonException If deserialization fails.
+ */
+ public static function deserializeArray(array $data, array $type): array
+ {
+ return Utils::isMapType($type)
+ ? self::deserializeMap($data, $type)
+ : self::deserializeList($data, $type);
+ }
+
+ /**
+ * Deserializes a value based on its type definition.
+ *
+ * @param mixed $data The data to deserialize.
+ * @param mixed $type The type definition.
+ * @return mixed The deserialized value.
+ * @throws JsonException If deserialization fails.
+ */
+ private static function deserializeValue(mixed $data, mixed $type): mixed
+ {
+ if ($type instanceof Union) {
+ return self::deserializeUnion($data, $type);
+ }
+
+ if (is_array($type)) {
+ return self::deserializeArray((array)$data, $type);
+ }
+
+ if (gettype($type) !== "string") {
+ throw new JsonException("Unexpected non-string type.");
+ }
+
+ return self::deserializeSingleValue($data, $type);
+ }
+
+ /**
+ * Deserializes a value based on the possible types in a union type definition.
+ *
+ * @param mixed $data The data to deserialize.
+ * @param Union $type The union type definition.
+ * @return mixed The deserialized value.
+ * @throws JsonException If none of the union types can successfully deserialize the value.
+ */
+ public static function deserializeUnion(mixed $data, Union $type): mixed
+ {
+ foreach ($type->types as $unionType) {
+ try {
+ return self::deserializeValue($data, $unionType);
+ } catch (\Throwable) {
+ // Catching Throwable instead of Exception to handle TypeError
+ // that occurs when assigning null to non-nullable typed properties
+ continue;
+ }
+ }
+ $readableType = Utils::getReadableType($data);
+ throw new JsonException(
+ "Cannot deserialize value of type $readableType with any of the union types: $type"
+ );
+ }
+
+ /**
+ * Deserializes a single value based on its expected type.
+ *
+ * @param mixed $data The data to deserialize.
+ * @param string $type The expected type.
+ * @return mixed The deserialized value.
+ * @throws JsonException If deserialization fails.
+ */
+ private static function deserializeSingleValue(mixed $data, string $type): mixed
+ {
+ if ($type === 'null' && $data === null) {
+ return null;
+ }
+
+ if ($type === 'date' && is_string($data)) {
+ return self::deserializeDate($data);
+ }
+
+ if ($type === 'datetime' && is_string($data)) {
+ return self::deserializeDateTime($data);
+ }
+
+ if ($type === 'mixed') {
+ return $data;
+ }
+
+ if (class_exists($type) && is_array($data)) {
+ /** @var array $data */
+ return self::deserializeObject($data, $type);
+ }
+
+ // Handle floats as a special case since gettype($data) returns "double" for float values in PHP, and because
+ // floats make come through from json_decoded as integers
+ if ($type === 'float' && (is_numeric($data))) {
+ return (float) $data;
+ }
+
+ // Handle bools as a special case since gettype($data) returns "boolean" for bool values in PHP.
+ if ($type === 'bool' && is_bool($data)) {
+ return $data;
+ }
+
+ if (gettype($data) === $type) {
+ return $data;
+ }
+
+ throw new JsonException("Unable to deserialize value of type '" . gettype($data) . "' as '$type'.");
+ }
+
+ /**
+ * Deserializes an array into an object of the given type.
+ *
+ * @param array $data The data to deserialize.
+ * @param string $type The class name of the object to deserialize into.
+ *
+ * @return object The deserialized object.
+ *
+ * @throws JsonException If the type does not implement JsonSerializableType.
+ */
+ public static function deserializeObject(array $data, string $type): object
+ {
+ if (!is_subclass_of($type, JsonSerializableType::class)) {
+ throw new JsonException("$type is not a subclass of JsonSerializableType.");
+ }
+ return $type::jsonDeserialize($data);
+ }
+
+ /**
+ * Deserializes a map (associative array) with defined key and value types.
+ *
+ * @param array $data The associative array to deserialize.
+ * @param array $type The type definition for the map.
+ * @return array The deserialized map.
+ * @throws JsonException If deserialization fails.
+ */
+ private static function deserializeMap(array $data, array $type): array
+ {
+ $keyType = array_key_first($type);
+ if ($keyType === null) {
+ throw new JsonException("Unexpected no key in ArrayType.");
+ }
+ $keyType = (string) $keyType;
+ $valueType = $type[$keyType];
+ /** @var array $result */
+ $result = [];
+
+ foreach ($data as $key => $item) {
+ $key = (string) Utils::castKey($key, $keyType);
+ $result[$key] = self::deserializeValue($item, $valueType);
+ }
+
+ return $result;
+ }
+
+ /**
+ * Deserializes a list (indexed array) with a defined value type.
+ *
+ * @param array $data The list to deserialize.
+ * @param array $type The type definition for the list.
+ * @return array The deserialized list.
+ * @throws JsonException If deserialization fails.
+ */
+ private static function deserializeList(array $data, array $type): array
+ {
+ $valueType = $type[0];
+ /** @var array */
+ return array_map(fn ($item) => self::deserializeValue($item, $valueType), $data);
+ }
+}
diff --git a/src/Core/Json/JsonEncoder.php b/src/Core/Json/JsonEncoder.php
new file mode 100644
index 0000000..34ff9cf
--- /dev/null
+++ b/src/Core/Json/JsonEncoder.php
@@ -0,0 +1,20 @@
+ Extra properties from JSON that don't map to class properties */
+ private array $__additionalProperties = [];
+
+ /** @var array Properties that have been explicitly set via setter methods */
+ private array $__explicitlySetProperties = [];
+
+ /**
+ * Serializes the object to a JSON string.
+ *
+ * @return string JSON-encoded string representation of the object.
+ * @throws Exception If encoding fails.
+ */
+ public function toJson(): string
+ {
+ $serializedObject = $this->jsonSerialize();
+ $encoded = JsonEncoder::encode(empty($serializedObject) ? new \stdClass() : $serializedObject);
+ if (!$encoded) {
+ throw new Exception("Could not encode type");
+ }
+ return $encoded;
+ }
+
+ /**
+ * Serializes the object to an array.
+ *
+ * @return mixed[] Array representation of the object.
+ * @throws JsonException If serialization fails.
+ */
+ public function jsonSerialize(): array
+ {
+ $result = [];
+ $reflectionClass = new \ReflectionClass($this);
+ foreach ($reflectionClass->getProperties() as $property) {
+ $jsonKey = self::getJsonKey($property);
+ if ($jsonKey === null) {
+ continue;
+ }
+ $value = $property->getValue($this);
+
+ // Handle DateTime properties
+ $dateTypeAttr = $property->getAttributes(Date::class)[0] ?? null;
+ if ($dateTypeAttr && $value instanceof DateTime) {
+ $dateType = $dateTypeAttr->newInstance()->type;
+ $value = ($dateType === Date::TYPE_DATE)
+ ? JsonSerializer::serializeDate($value)
+ : JsonSerializer::serializeDateTime($value);
+ }
+
+ // Handle Union annotations
+ $unionTypeAttr = $property->getAttributes(Union::class)[0] ?? null;
+ if ($unionTypeAttr) {
+ $unionType = $unionTypeAttr->newInstance();
+ $value = JsonSerializer::serializeUnion($value, $unionType);
+ }
+
+ // Handle arrays with type annotations
+ $arrayTypeAttr = $property->getAttributes(ArrayType::class)[0] ?? null;
+ if ($arrayTypeAttr && is_array($value)) {
+ $arrayType = $arrayTypeAttr->newInstance()->type;
+ $value = JsonSerializer::serializeArray($value, $arrayType);
+ }
+
+ // Handle object (skip stdClass since it's already serialized, e.g. from union processing)
+ if (is_object($value) && !($value instanceof \stdClass)) {
+ $value = JsonSerializer::serializeObject($value);
+ }
+
+ // Include the value if it's not null, OR if it was explicitly set (even to null)
+ if ($value !== null || array_key_exists($property->getName(), $this->__explicitlySetProperties)) {
+ $result[$jsonKey] = $value;
+ }
+ }
+ return $result;
+ }
+
+ /**
+ * Deserializes a JSON string into an instance of the calling class.
+ *
+ * @param string $json JSON string to deserialize.
+ * @return static Deserialized object.
+ * @throws JsonException If decoding fails or the result is not an array.
+ * @throws Exception If deserialization fails.
+ */
+ public static function fromJson(string $json): static
+ {
+ $decodedJson = JsonDecoder::decode($json);
+ if (!is_array($decodedJson)) {
+ throw new JsonException("Unexpected non-array decoded type: " . gettype($decodedJson));
+ }
+ /** @var array $decodedJson */
+ // static:: (not self::) so subclasses' typed jsonDeserialize overrides are dispatched via late static binding.
+ return static::jsonDeserialize($decodedJson);
+ }
+
+ /**
+ * Deserializes an array into an instance of the calling class.
+ *
+ * @param array $data Array data to deserialize.
+ * @return static Deserialized object.
+ * @throws JsonException If deserialization fails.
+ */
+ public static function jsonDeserialize(array $data): static
+ {
+ $reflectionClass = new \ReflectionClass(static::class);
+ $constructor = $reflectionClass->getConstructor();
+ if ($constructor === null) {
+ throw new JsonException("No constructor found.");
+ }
+
+ $args = [];
+ $properties = [];
+ $additionalProperties = [];
+ foreach ($reflectionClass->getProperties() as $property) {
+ $jsonKey = self::getJsonKey($property) ?? $property->getName();
+ $properties[$jsonKey] = $property;
+ }
+
+ foreach ($data as $jsonKey => $value) {
+ if (!isset($properties[$jsonKey])) {
+ // This JSON key doesn't map to any class property - add it to additionalProperties
+ $additionalProperties[$jsonKey] = $value;
+ continue;
+ }
+
+ $property = $properties[$jsonKey];
+
+ // Handle Date annotation
+ $dateTypeAttr = $property->getAttributes(Date::class)[0] ?? null;
+ if ($dateTypeAttr) {
+ $dateType = $dateTypeAttr->newInstance()->type;
+ if (!is_string($value)) {
+ throw new JsonException("Unexpected non-string type for date.");
+ }
+ $value = ($dateType === Date::TYPE_DATE)
+ ? JsonDeserializer::deserializeDate($value)
+ : JsonDeserializer::deserializeDateTime($value);
+ }
+
+ // Handle Array annotation
+ $arrayTypeAttr = $property->getAttributes(ArrayType::class)[0] ?? null;
+ if (is_array($value) && $arrayTypeAttr) {
+ $arrayType = $arrayTypeAttr->newInstance()->type;
+ $value = JsonDeserializer::deserializeArray($value, $arrayType);
+ }
+
+ // Handle Union annotations
+ $unionTypeAttr = $property->getAttributes(Union::class)[0] ?? null;
+ if ($unionTypeAttr) {
+ $unionType = $unionTypeAttr->newInstance();
+ $value = JsonDeserializer::deserializeUnion($value, $unionType);
+ }
+
+ // Handle object
+ $type = $property->getType();
+ if (is_array($value) && $type instanceof ReflectionNamedType && !$type->isBuiltin()) {
+ /** @var array $arrayValue */
+ $arrayValue = $value;
+ $value = JsonDeserializer::deserializeObject($arrayValue, $type->getName());
+ }
+
+ $args[$property->getName()] = $value;
+ }
+
+ // Fill in any missing properties with defaults
+ foreach ($properties as $property) {
+ if (!isset($args[$property->getName()])) {
+ $args[$property->getName()] = $property->hasDefaultValue() ? $property->getDefaultValue() : null;
+ }
+ }
+
+ // @phpstan-ignore-next-line
+ $result = new static($args);
+ $result->__additionalProperties = $additionalProperties;
+ return $result;
+ }
+
+ /**
+ * Get properties from JSON that weren't mapped to class fields
+ * @return array
+ */
+ public function getAdditionalProperties(): array
+ {
+ return $this->__additionalProperties;
+ }
+
+ /**
+ * Mark a property as explicitly set.
+ * This ensures the property will be included in JSON serialization even if null.
+ *
+ * @param string $propertyName The name of the property to mark as explicitly set.
+ */
+ protected function _setField(string $propertyName): void
+ {
+ $this->__explicitlySetProperties[$propertyName] = true;
+ }
+
+ /**
+ * Retrieves the JSON key associated with a property.
+ *
+ * @param ReflectionProperty $property The reflection property.
+ * @return ?string The JSON key, or null if not available.
+ */
+ private static function getJsonKey(ReflectionProperty $property): ?string
+ {
+ $jsonPropertyAttr = $property->getAttributes(JsonProperty::class)[0] ?? null;
+ return $jsonPropertyAttr?->newInstance()?->name;
+ }
+}
diff --git a/src/Core/Json/JsonSerializer.php b/src/Core/Json/JsonSerializer.php
new file mode 100644
index 0000000..58ec2f2
--- /dev/null
+++ b/src/Core/Json/JsonSerializer.php
@@ -0,0 +1,212 @@
+format(Constant::DateFormat);
+ }
+
+ /**
+ * Serializes a DateTime object into a string using the date-time format.
+ * Normalizes UTC times to use 'Z' suffix instead of '+00:00'.
+ *
+ * @param DateTime $date The DateTime object to serialize.
+ * @return string The serialized date-time string.
+ */
+ public static function serializeDateTime(DateTime $date): string
+ {
+ $formatted = $date->format(Constant::DateTimeFormat);
+ if (str_ends_with($formatted, '+00:00')) {
+ return substr($formatted, 0, -6) . 'Z';
+ }
+ return $formatted;
+ }
+
+ /**
+ * Serializes an array based on type annotations (either a list or map).
+ *
+ * @param array $data The array to be serialized.
+ * @param array $type The type definition from the annotation.
+ * @return array|\stdClass The serialized array, or stdClass if empty map.
+ * @throws JsonException If serialization fails.
+ */
+ public static function serializeArray(array $data, array $type): array|\stdClass
+ {
+ return Utils::isMapType($type)
+ ? self::serializeMap($data, $type)
+ : self::serializeList($data, $type);
+ }
+
+ /**
+ * Serializes a value based on its type definition.
+ *
+ * @param mixed $data The value to serialize.
+ * @param mixed $type The type definition.
+ * @return mixed The serialized value.
+ * @throws JsonException If serialization fails.
+ */
+ private static function serializeValue(mixed $data, mixed $type): mixed
+ {
+ if ($type instanceof Union) {
+ return self::serializeUnion($data, $type);
+ }
+
+ if (is_array($type)) {
+ return self::serializeArray((array)$data, $type);
+ }
+
+ if (gettype($type) !== "string") {
+ throw new JsonException("Unexpected non-string type.");
+ }
+
+ return self::serializeSingleValue($data, $type);
+ }
+
+ /**
+ * Serializes a value for a union type definition.
+ *
+ * @param mixed $data The value to serialize.
+ * @param Union $unionType The union type definition.
+ * @return mixed The serialized value.
+ * @throws JsonException If serialization fails for all union types.
+ */
+ public static function serializeUnion(mixed $data, Union $unionType): mixed
+ {
+ foreach ($unionType->types as $type) {
+ try {
+ return self::serializeValue($data, $type);
+ } catch (Exception) {
+ // Try the next type in the union
+ continue;
+ }
+ }
+ $readableType = Utils::getReadableType($data);
+ throw new JsonException(
+ "Cannot serialize value of type $readableType with any of the union types: $unionType"
+ );
+ }
+
+ /**
+ * Serializes a single value based on its type.
+ *
+ * @param mixed $data The value to serialize.
+ * @param string $type The expected type.
+ * @return mixed The serialized value.
+ * @throws JsonException If serialization fails.
+ */
+ private static function serializeSingleValue(mixed $data, string $type): mixed
+ {
+ if ($type === 'null' && $data === null) {
+ return null;
+ }
+
+ if (($type === 'date' || $type === 'datetime') && $data instanceof DateTime) {
+ return $type === 'date' ? self::serializeDate($data) : self::serializeDateTime($data);
+ }
+
+ if ($type === 'mixed') {
+ return $data;
+ }
+
+ if (class_exists($type) && $data instanceof $type) {
+ return self::serializeObject($data);
+ }
+
+ // Handle floats as a special case since gettype($data) returns "double" for float values in PHP.
+ if ($type === 'float' && is_float($data)) {
+ return $data;
+ }
+
+ // Handle bools as a special case since gettype($data) returns "boolean" for bool values in PHP.
+ if ($type === 'bool' && is_bool($data)) {
+ return $data;
+ }
+
+ if (gettype($data) === $type) {
+ return $data;
+ }
+
+ throw new JsonException("Unable to serialize value of type '" . gettype($data) . "' as '$type'.");
+ }
+
+ /**
+ * Serializes an object to a JSON-serializable format.
+ *
+ * @param object $data The object to serialize.
+ * @return mixed The serialized data.
+ * @throws JsonException If the object does not implement JsonSerializable.
+ */
+ public static function serializeObject(object $data): mixed
+ {
+ if (!is_subclass_of($data, JsonSerializable::class)) {
+ $type = get_class($data);
+ throw new JsonException("Class $type must implement JsonSerializable.");
+ }
+ $result = $data->jsonSerialize();
+ if (is_array($result) && empty($result)) {
+ return new \stdClass();
+ }
+ return $result;
+ }
+
+ /**
+ * Serializes a map (associative array) with defined key and value types.
+ *
+ * @param array $data The associative array to serialize.
+ * @param array $type The type definition for the map.
+ * @return array|\stdClass The serialized map, or stdClass if empty.
+ * @throws JsonException If serialization fails.
+ */
+ private static function serializeMap(array $data, array $type): array|\stdClass
+ {
+ if (empty($data)) {
+ return new \stdClass();
+ }
+ $keyType = array_key_first($type);
+ if ($keyType === null) {
+ throw new JsonException("Unexpected no key in ArrayType.");
+ }
+ $keyType = (string) $keyType;
+ $valueType = $type[$keyType];
+ /** @var array $result */
+ $result = [];
+
+ foreach ($data as $key => $item) {
+ $key = (string) Utils::castKey($key, $keyType);
+ $result[$key] = self::serializeValue($item, $valueType);
+ }
+
+ return $result;
+ }
+
+ /**
+ * Serializes a list (indexed array) where only the value type is defined.
+ *
+ * @param array $data The list to serialize.
+ * @param array $type The type definition for the list.
+ * @return array The serialized list.
+ * @throws JsonException If serialization fails.
+ */
+ private static function serializeList(array $data, array $type): array
+ {
+ $valueType = $type[0];
+ /** @var array */
+ return array_map(fn ($item) => self::serializeValue($item, $valueType), $data);
+ }
+}
diff --git a/src/Core/Json/Utils.php b/src/Core/Json/Utils.php
new file mode 100644
index 0000000..3daf656
--- /dev/null
+++ b/src/Core/Json/Utils.php
@@ -0,0 +1,62 @@
+ $type The type definition from the annotation.
+ * @return bool True if the type is a map, false if it's a list.
+ */
+ public static function isMapType(array $type): bool
+ {
+ return count($type) === 1 && !array_is_list($type);
+ }
+
+ /**
+ * Casts the key to the appropriate type based on the key type.
+ *
+ * @param mixed $key The key to be cast.
+ * @param string $keyType The type to cast the key to ('string', 'integer', 'float').
+ * @return int|string The casted key.
+ * @throws JsonException
+ */
+ public static function castKey(mixed $key, string $keyType): int|string
+ {
+ if (!is_scalar($key)) {
+ throw new JsonException("Key must be a scalar type.");
+ }
+ return match ($keyType) {
+ 'integer' => (int)$key,
+ // PHP arrays don't support float keys; truncate to int
+ 'float' => (int)$key,
+ 'string' => (string)$key,
+ default => is_int($key) ? $key : (string)$key,
+ };
+ }
+
+ /**
+ * Returns a human-readable representation of the input's type.
+ *
+ * @param mixed $input The input value to determine the type of.
+ * @return string A readable description of the input type.
+ */
+ public static function getReadableType(mixed $input): string
+ {
+ if (is_object($input)) {
+ return get_class($input);
+ } elseif (is_array($input)) {
+ return 'array(' . count($input) . ' items)';
+ } elseif (is_null($input)) {
+ return 'null';
+ } else {
+ return gettype($input);
+ }
+ }
+}
diff --git a/src/Core/Multipart/MultipartApiRequest.php b/src/Core/Multipart/MultipartApiRequest.php
new file mode 100644
index 0000000..7cae3d5
--- /dev/null
+++ b/src/Core/Multipart/MultipartApiRequest.php
@@ -0,0 +1,28 @@
+ $headers Additional headers for the request (optional)
+ * @param array $query Query parameters for the request (optional)
+ * @param ?MultipartFormData $body The multipart form data for the request (optional)
+ */
+ public function __construct(
+ string $baseUrl,
+ string $path,
+ HttpMethod $method,
+ array $headers = [],
+ array $query = [],
+ public readonly ?MultipartFormData $body = null
+ ) {
+ parent::__construct($baseUrl, $path, $method, $headers, $query);
+ }
+}
diff --git a/src/Core/Multipart/MultipartFormData.php b/src/Core/Multipart/MultipartFormData.php
new file mode 100644
index 0000000..cb695a4
--- /dev/null
+++ b/src/Core/Multipart/MultipartFormData.php
@@ -0,0 +1,58 @@
+
+ */
+ private array $parts = [];
+
+ /**
+ * Adds a new part to the multipart form data.
+ *
+ * @param string $name
+ * @param string|int|bool|float|StreamInterface $value
+ * @param ?string $contentType
+ */
+ public function add(
+ string $name,
+ string|int|bool|float|StreamInterface $value,
+ ?string $contentType = null,
+ ): void {
+ $headers = $contentType !== null ? ['Content-Type' => $contentType] : null;
+ $this->addPart(
+ new MultipartFormDataPart(
+ name: $name,
+ value: $value,
+ headers: $headers,
+ )
+ );
+ }
+
+ /**
+ * Adds a new part to the multipart form data.
+ *
+ * @param MultipartFormDataPart $part
+ */
+ public function addPart(MultipartFormDataPart $part): void
+ {
+ $this->parts[] = $part;
+ }
+
+ /**
+ * Adds all parts to a MultipartStreamBuilder.
+ *
+ * @param MultipartStreamBuilder $builder
+ */
+ public function addToBuilder(MultipartStreamBuilder $builder): void
+ {
+ foreach ($this->parts as $part) {
+ $part->addToBuilder($builder);
+ }
+ }
+}
diff --git a/src/Core/Multipart/MultipartFormDataPart.php b/src/Core/Multipart/MultipartFormDataPart.php
new file mode 100644
index 0000000..95f4a47
--- /dev/null
+++ b/src/Core/Multipart/MultipartFormDataPart.php
@@ -0,0 +1,62 @@
+
+ */
+ private ?array $headers;
+
+ /**
+ * @param string $name
+ * @param string|bool|float|int|StreamInterface $value
+ * @param ?string $filename
+ * @param ?array $headers
+ */
+ public function __construct(
+ string $name,
+ string|bool|float|int|StreamInterface $value,
+ ?string $filename = null,
+ ?array $headers = null
+ ) {
+ $this->name = $name;
+ $this->contents = $value instanceof StreamInterface ? $value : (string)$value;
+ $this->filename = $filename;
+ $this->headers = $headers;
+ }
+
+ /**
+ * Adds this part to a MultipartStreamBuilder.
+ *
+ * @param MultipartStreamBuilder $builder
+ */
+ public function addToBuilder(MultipartStreamBuilder $builder): void
+ {
+ $options = array_filter([
+ 'filename' => $this->filename,
+ 'headers' => $this->headers,
+ ], fn ($value) => $value !== null);
+
+ $builder->addResource($this->name, $this->contents, $options);
+ }
+}
diff --git a/src/Core/Types/ArrayType.php b/src/Core/Types/ArrayType.php
new file mode 100644
index 0000000..a1dd0a2
--- /dev/null
+++ b/src/Core/Types/ArrayType.php
@@ -0,0 +1,16 @@
+ 'valueType'] for maps, or ['valueType'] for lists
+ */
+ public function __construct(public array $type)
+ {
+ }
+}
diff --git a/src/Core/Types/Constant.php b/src/Core/Types/Constant.php
new file mode 100644
index 0000000..757518c
--- /dev/null
+++ b/src/Core/Types/Constant.php
@@ -0,0 +1,12 @@
+> The types allowed for this property, which can be strings, arrays, or nested Union types.
+ */
+ public array $types;
+
+ /**
+ * Constructor for the Union attribute.
+ *
+ * @param string|Union|array ...$types The list of types that the property can accept.
+ * This can include primitive types (e.g., 'string', 'int'), arrays, or other Union instances.
+ *
+ * Example:
+ * ```php
+ * #[Union('string', 'null', 'date', new Union('boolean', 'int'))]
+ * ```
+ */
+ public function __construct(string|Union|array ...$types)
+ {
+ $this->types = $types;
+ }
+
+ /**
+ * Converts the Union type to a string representation.
+ *
+ * @return string A string representation of the union types.
+ */
+ public function __toString(): string
+ {
+ return implode(' | ', array_map(function ($type) {
+ if (is_string($type)) {
+ return $type;
+ } elseif ($type instanceof Union) {
+ return (string) $type; // Recursively handle nested unions
+ } elseif (is_array($type)) {
+ return 'array'; // Handle arrays
+ }
+ }, $this->types));
+ }
+}
diff --git a/src/Deployment/DeploymentClient.php b/src/Deployment/DeploymentClient.php
new file mode 100644
index 0000000..9a3824b
--- /dev/null
+++ b/src/Deployment/DeploymentClient.php
@@ -0,0 +1,100 @@
+,
+ * } $options @phpstan-ignore-next-line Property is used in endpoint methods via HttpEndpointGenerator
+ */
+ private array $options;
+
+ /**
+ * @var RawClient $client
+ */
+ private RawClient $client;
+
+ /**
+ * @param RawClient $client
+ * @param ?array{
+ * baseUrl?: string,
+ * client?: ClientInterface,
+ * maxRetries?: int,
+ * timeout?: float,
+ * headers?: array,
+ * } $options
+ */
+ public function __construct(
+ RawClient $client,
+ ?array $options = null,
+ ) {
+ $this->client = $client;
+ $this->options = $options ?? [];
+ }
+
+ /**
+ * Example:
+ * ```php
+ * $client->deployment->licenseStatus();
+ * ```
+ *
+ * @param ?array{
+ * baseUrl?: string,
+ * maxRetries?: int,
+ * timeout?: float,
+ * headers?: array,
+ * queryParameters?: array,
+ * bodyProperties?: array,
+ * } $options
+ * @return ?DeploymentLicenseStatusResponse
+ * @throws CloudpdfException
+ * @throws CloudpdfApiException
+ */
+ public function licenseStatus(?array $options = null): ?DeploymentLicenseStatusResponse
+ {
+ $options = array_merge($this->options, $options ?? []);
+ try {
+ $response = $this->client->sendRequest(
+ new JsonApiRequest(
+ baseUrl: $options['baseUrl'] ?? $this->client->options['baseUrl'] ?? '',
+ path: "v1/deployment/license/status",
+ method: HttpMethod::GET,
+ ),
+ $options,
+ );
+ $statusCode = $response->getStatusCode();
+ if ($statusCode >= 200 && $statusCode < 400) {
+ $json = $response->getBody()->getContents();
+ if (empty($json)) {
+ return null;
+ }
+ return DeploymentLicenseStatusResponse::fromJson($json);
+ }
+ } catch (JsonException $e) {
+ throw new CloudpdfException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e);
+ } catch (ClientExceptionInterface $e) {
+ throw new CloudpdfException(message: $e->getMessage(), previous: $e);
+ }
+ throw new CloudpdfApiException(
+ message: 'API request failed',
+ statusCode: $statusCode,
+ body: $response->getBody()->getContents(),
+ );
+ }
+}
diff --git a/src/Doc/Annotations/AnnotationsClient.php b/src/Doc/Annotations/AnnotationsClient.php
new file mode 100644
index 0000000..b58a1ef
--- /dev/null
+++ b/src/Doc/Annotations/AnnotationsClient.php
@@ -0,0 +1,329 @@
+,
+ * } $options @phpstan-ignore-next-line Property is used in endpoint methods via HttpEndpointGenerator
+ */
+ private array $options;
+
+ /**
+ * @var RawClient $client
+ */
+ private RawClient $client;
+
+ /**
+ * @param RawClient $client
+ * @param ?array{
+ * baseUrl?: string,
+ * client?: ClientInterface,
+ * maxRetries?: int,
+ * timeout?: float,
+ * headers?: array,
+ * } $options
+ */
+ public function __construct(
+ RawClient $client,
+ ?array $options = null,
+ ) {
+ $this->client = $client;
+ $this->options = $options ?? [];
+ }
+
+ /**
+ * Example:
+ * ```php
+ * $client->doc->annotations->list(
+ * 'docId',
+ * 'layerName',
+ * 1,
+ * new ListAnnotationsRequest([]),
+ * );
+ * ```
+ *
+ * @param string $docId
+ * @param string $layerName
+ * @param int $pon
+ * @param ListAnnotationsRequest $request
+ * @param ?array{
+ * baseUrl?: string,
+ * maxRetries?: int,
+ * timeout?: float,
+ * headers?: array,
+ * queryParameters?: array,
+ * bodyProperties?: array,
+ * } $options
+ * @return ?DocAnnotationsList200Response
+ * @throws CloudpdfException
+ * @throws CloudpdfApiException
+ */
+ public function list(string $docId, string $layerName, int $pon, ListAnnotationsRequest $request = new ListAnnotationsRequest(), ?array $options = null): ?DocAnnotationsList200Response
+ {
+ $options = array_merge($this->options, $options ?? []);
+ $headers = [];
+ if ($request->documentPassword != null) {
+ $headers['X-Document-Password'] = $request->documentPassword;
+ }
+ try {
+ $response = $this->client->sendRequest(
+ new JsonApiRequest(
+ baseUrl: $options['baseUrl'] ?? $this->client->options['baseUrl'] ?? '',
+ path: "v1/docs/{$docId}/layers/{$layerName}/annotations/pages/{$pon}/items",
+ method: HttpMethod::GET,
+ headers: $headers,
+ ),
+ $options,
+ );
+ $statusCode = $response->getStatusCode();
+ if ($statusCode >= 200 && $statusCode < 400) {
+ $json = $response->getBody()->getContents();
+ if (empty($json)) {
+ return null;
+ }
+ return DocAnnotationsList200Response::fromJson($json);
+ }
+ } catch (JsonException $e) {
+ throw new CloudpdfException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e);
+ } catch (ClientExceptionInterface $e) {
+ throw new CloudpdfException(message: $e->getMessage(), previous: $e);
+ }
+ throw new CloudpdfApiException(
+ message: 'API request failed',
+ statusCode: $statusCode,
+ body: $response->getBody()->getContents(),
+ );
+ }
+
+ /**
+ * Doc JWTs may instead carry collab scopes (annotations:create:self, …) that refine per-annotation authorship rules; the API token is exempt from both.
+ *
+ * Example:
+ * ```php
+ * $client->doc->annotations->create(
+ * 'docId',
+ * 'layerName',
+ * 1,
+ * new CreateAnnotationsRequest([
+ * 'body' => [
+ * 'key' => "value",
+ * ],
+ * ]),
+ * );
+ * ```
+ *
+ * @param string $docId
+ * @param string $layerName
+ * @param int $pon
+ * @param CreateAnnotationsRequest $request
+ * @param ?array{
+ * baseUrl?: string,
+ * maxRetries?: int,
+ * timeout?: float,
+ * headers?: array,
+ * queryParameters?: array,
+ * bodyProperties?: array,
+ * } $options
+ * @return ?DocAnnotationsCreate200Response
+ * @throws CloudpdfException
+ * @throws CloudpdfApiException
+ */
+ public function create(string $docId, string $layerName, int $pon, CreateAnnotationsRequest $request, ?array $options = null): ?DocAnnotationsCreate200Response
+ {
+ $options = array_merge($this->options, $options ?? []);
+ $headers = [];
+ if ($request->documentPassword != null) {
+ $headers['X-Document-Password'] = $request->documentPassword;
+ }
+ try {
+ $response = $this->client->sendRequest(
+ new JsonApiRequest(
+ baseUrl: $options['baseUrl'] ?? $this->client->options['baseUrl'] ?? '',
+ path: "v1/docs/{$docId}/layers/{$layerName}/annotations/pages/{$pon}/items",
+ method: HttpMethod::POST,
+ headers: $headers,
+ body: $request->body,
+ ),
+ $options,
+ );
+ $statusCode = $response->getStatusCode();
+ if ($statusCode >= 200 && $statusCode < 400) {
+ $json = $response->getBody()->getContents();
+ if (empty($json)) {
+ return null;
+ }
+ return DocAnnotationsCreate200Response::fromJson($json);
+ }
+ } catch (JsonException $e) {
+ throw new CloudpdfException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e);
+ } catch (ClientExceptionInterface $e) {
+ throw new CloudpdfException(message: $e->getMessage(), previous: $e);
+ }
+ throw new CloudpdfApiException(
+ message: 'API request failed',
+ statusCode: $statusCode,
+ body: $response->getBody()->getContents(),
+ );
+ }
+
+ /**
+ * Example:
+ * ```php
+ * $client->doc->annotations->delete(
+ * 'docId',
+ * 'layerName',
+ * 1,
+ * 'annotKey',
+ * new DeleteAnnotationsRequest([]),
+ * );
+ * ```
+ *
+ * @param string $docId
+ * @param string $layerName
+ * @param int $pon
+ * @param string $annotKey
+ * @param DeleteAnnotationsRequest $request
+ * @param ?array{
+ * baseUrl?: string,
+ * maxRetries?: int,
+ * timeout?: float,
+ * headers?: array,
+ * queryParameters?: array,
+ * bodyProperties?: array,
+ * } $options
+ * @return ?DocAnnotationsDelete200Response
+ * @throws CloudpdfException
+ * @throws CloudpdfApiException
+ */
+ public function delete(string $docId, string $layerName, int $pon, string $annotKey, DeleteAnnotationsRequest $request = new DeleteAnnotationsRequest(), ?array $options = null): ?DocAnnotationsDelete200Response
+ {
+ $options = array_merge($this->options, $options ?? []);
+ $headers = [];
+ if ($request->documentPassword != null) {
+ $headers['X-Document-Password'] = $request->documentPassword;
+ }
+ try {
+ $response = $this->client->sendRequest(
+ new JsonApiRequest(
+ baseUrl: $options['baseUrl'] ?? $this->client->options['baseUrl'] ?? '',
+ path: "v1/docs/{$docId}/layers/{$layerName}/annotations/pages/{$pon}/items/{$annotKey}",
+ method: HttpMethod::DELETE,
+ headers: $headers,
+ ),
+ $options,
+ );
+ $statusCode = $response->getStatusCode();
+ if ($statusCode >= 200 && $statusCode < 400) {
+ $json = $response->getBody()->getContents();
+ if (empty($json)) {
+ return null;
+ }
+ return DocAnnotationsDelete200Response::fromJson($json);
+ }
+ } catch (JsonException $e) {
+ throw new CloudpdfException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e);
+ } catch (ClientExceptionInterface $e) {
+ throw new CloudpdfException(message: $e->getMessage(), previous: $e);
+ }
+ throw new CloudpdfApiException(
+ message: 'API request failed',
+ statusCode: $statusCode,
+ body: $response->getBody()->getContents(),
+ );
+ }
+
+ /**
+ * Example:
+ * ```php
+ * $client->doc->annotations->update(
+ * 'docId',
+ * 'layerName',
+ * 1,
+ * 'annotKey',
+ * new UpdateAnnotationsRequest([
+ * 'body' => [
+ * 'key' => "value",
+ * ],
+ * ]),
+ * );
+ * ```
+ *
+ * @param string $docId
+ * @param string $layerName
+ * @param int $pon
+ * @param string $annotKey
+ * @param UpdateAnnotationsRequest $request
+ * @param ?array{
+ * baseUrl?: string,
+ * maxRetries?: int,
+ * timeout?: float,
+ * headers?: array,
+ * queryParameters?: array,
+ * bodyProperties?: array,
+ * } $options
+ * @return ?DocAnnotationsUpdate200Response
+ * @throws CloudpdfException
+ * @throws CloudpdfApiException
+ */
+ public function update(string $docId, string $layerName, int $pon, string $annotKey, UpdateAnnotationsRequest $request, ?array $options = null): ?DocAnnotationsUpdate200Response
+ {
+ $options = array_merge($this->options, $options ?? []);
+ $headers = [];
+ if ($request->documentPassword != null) {
+ $headers['X-Document-Password'] = $request->documentPassword;
+ }
+ try {
+ $response = $this->client->sendRequest(
+ new JsonApiRequest(
+ baseUrl: $options['baseUrl'] ?? $this->client->options['baseUrl'] ?? '',
+ path: "v1/docs/{$docId}/layers/{$layerName}/annotations/pages/{$pon}/items/{$annotKey}",
+ method: HttpMethod::PATCH,
+ headers: $headers,
+ body: $request->body,
+ ),
+ $options,
+ );
+ $statusCode = $response->getStatusCode();
+ if ($statusCode >= 200 && $statusCode < 400) {
+ $json = $response->getBody()->getContents();
+ if (empty($json)) {
+ return null;
+ }
+ return DocAnnotationsUpdate200Response::fromJson($json);
+ }
+ } catch (JsonException $e) {
+ throw new CloudpdfException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e);
+ } catch (ClientExceptionInterface $e) {
+ throw new CloudpdfException(message: $e->getMessage(), previous: $e);
+ }
+ throw new CloudpdfApiException(
+ message: 'API request failed',
+ statusCode: $statusCode,
+ body: $response->getBody()->getContents(),
+ );
+ }
+}
diff --git a/src/Doc/Annotations/Requests/CreateAnnotationsRequest.php b/src/Doc/Annotations/Requests/CreateAnnotationsRequest.php
new file mode 100644
index 0000000..9a42f74
--- /dev/null
+++ b/src/Doc/Annotations/Requests/CreateAnnotationsRequest.php
@@ -0,0 +1,31 @@
+ $body
+ */
+ public array $body;
+
+ /**
+ * @param array{
+ * body: array,
+ * documentPassword?: ?string,
+ * } $values
+ */
+ public function __construct(
+ array $values,
+ ) {
+ $this->documentPassword = $values['documentPassword'] ?? null;
+ $this->body = $values['body'];
+ }
+}
diff --git a/src/Doc/Annotations/Requests/DeleteAnnotationsRequest.php b/src/Doc/Annotations/Requests/DeleteAnnotationsRequest.php
new file mode 100644
index 0000000..fe64e97
--- /dev/null
+++ b/src/Doc/Annotations/Requests/DeleteAnnotationsRequest.php
@@ -0,0 +1,24 @@
+documentPassword = $values['documentPassword'] ?? null;
+ }
+}
diff --git a/src/Doc/Annotations/Requests/ListAnnotationsRequest.php b/src/Doc/Annotations/Requests/ListAnnotationsRequest.php
new file mode 100644
index 0000000..b6cb3e7
--- /dev/null
+++ b/src/Doc/Annotations/Requests/ListAnnotationsRequest.php
@@ -0,0 +1,24 @@
+documentPassword = $values['documentPassword'] ?? null;
+ }
+}
diff --git a/src/Doc/Annotations/Requests/UpdateAnnotationsRequest.php b/src/Doc/Annotations/Requests/UpdateAnnotationsRequest.php
new file mode 100644
index 0000000..fb9a67f
--- /dev/null
+++ b/src/Doc/Annotations/Requests/UpdateAnnotationsRequest.php
@@ -0,0 +1,31 @@
+ $body
+ */
+ public array $body;
+
+ /**
+ * @param array{
+ * body: array,
+ * documentPassword?: ?string,
+ * } $values
+ */
+ public function __construct(
+ array $values,
+ ) {
+ $this->documentPassword = $values['documentPassword'] ?? null;
+ $this->body = $values['body'];
+ }
+}
diff --git a/src/Doc/DocClient.php b/src/Doc/DocClient.php
new file mode 100644
index 0000000..e1f1736
--- /dev/null
+++ b/src/Doc/DocClient.php
@@ -0,0 +1,394 @@
+,
+ * } $options @phpstan-ignore-next-line Property is used in endpoint methods via HttpEndpointGenerator
+ */
+ private array $options;
+
+ /**
+ * @var RawClient $client
+ */
+ private RawClient $client;
+
+ /**
+ * @param RawClient $client
+ * @param ?array{
+ * baseUrl?: string,
+ * client?: ClientInterface,
+ * maxRetries?: int,
+ * timeout?: float,
+ * headers?: array,
+ * } $options
+ */
+ public function __construct(
+ RawClient $client,
+ ?array $options = null,
+ ) {
+ $this->client = $client;
+ $this->options = $options ?? [];
+ $this->annotations = new AnnotationsClient($this->client, $this->options);
+ $this->forms = new FormsClient($this->client, $this->options);
+ $this->metadata = new MetadataClient($this->client, $this->options);
+ $this->pages = new PagesClient($this->client, $this->options);
+ $this->redactions = new RedactionsClient($this->client, $this->options);
+ }
+
+ /**
+ * Example:
+ * ```php
+ * $client->doc->head(
+ * 'docId',
+ * new HeadDocRequest([]),
+ * );
+ * ```
+ *
+ * @param string $docId
+ * @param HeadDocRequest $request
+ * @param ?array{
+ * baseUrl?: string,
+ * maxRetries?: int,
+ * timeout?: float,
+ * headers?: array,
+ * queryParameters?: array,
+ * bodyProperties?: array,
+ * } $options
+ * @return ?DocHead200Response
+ * @throws CloudpdfException
+ * @throws CloudpdfApiException
+ */
+ public function head(string $docId, HeadDocRequest $request = new HeadDocRequest(), ?array $options = null): ?DocHead200Response
+ {
+ $options = array_merge($this->options, $options ?? []);
+ $headers = [];
+ if ($request->documentPassword != null) {
+ $headers['X-Document-Password'] = $request->documentPassword;
+ }
+ try {
+ $response = $this->client->sendRequest(
+ new JsonApiRequest(
+ baseUrl: $options['baseUrl'] ?? $this->client->options['baseUrl'] ?? '',
+ path: "v1/docs/{$docId}/head",
+ method: HttpMethod::GET,
+ headers: $headers,
+ ),
+ $options,
+ );
+ $statusCode = $response->getStatusCode();
+ if ($statusCode >= 200 && $statusCode < 400) {
+ $json = $response->getBody()->getContents();
+ if (empty($json)) {
+ return null;
+ }
+ return DocHead200Response::fromJson($json);
+ }
+ } catch (JsonException $e) {
+ throw new CloudpdfException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e);
+ } catch (ClientExceptionInterface $e) {
+ throw new CloudpdfException(message: $e->getMessage(), previous: $e);
+ }
+ throw new CloudpdfApiException(
+ message: 'API request failed',
+ statusCode: $statusCode,
+ body: $response->getBody()->getContents(),
+ );
+ }
+
+ /**
+ * Example:
+ * ```php
+ * $client->doc->download(
+ * 'docId',
+ * 'layerName',
+ * new DownloadDocRequest([]),
+ * );
+ * ```
+ *
+ * @param string $docId
+ * @param string $layerName
+ * @param DownloadDocRequest $request
+ * @param ?array{
+ * baseUrl?: string,
+ * maxRetries?: int,
+ * timeout?: float,
+ * headers?: array,
+ * queryParameters?: array,
+ * bodyProperties?: array,
+ * } $options
+ * @return string
+ * @throws CloudpdfException
+ * @throws CloudpdfApiException
+ */
+ public function download(string $docId, string $layerName, DownloadDocRequest $request = new DownloadDocRequest(), ?array $options = null): string
+ {
+ $options = array_merge($this->options, $options ?? []);
+ $headers = [];
+ if ($request->documentPassword != null) {
+ $headers['X-Document-Password'] = $request->documentPassword;
+ }
+ try {
+ $response = $this->client->sendRequest(
+ new JsonApiRequest(
+ baseUrl: $options['baseUrl'] ?? $this->client->options['baseUrl'] ?? '',
+ path: "v1/docs/{$docId}/layers/{$layerName}/download",
+ method: HttpMethod::GET,
+ headers: $headers,
+ ),
+ $options,
+ );
+ $statusCode = $response->getStatusCode();
+ if ($statusCode >= 200 && $statusCode < 400) {
+ return $response->getBody()->getContents();
+ }
+ } catch (ClientExceptionInterface $e) {
+ throw new CloudpdfException(message: $e->getMessage(), previous: $e);
+ }
+ throw new CloudpdfApiException(
+ message: 'API request failed',
+ statusCode: $statusCode,
+ body: $response->getBody()->getContents(),
+ );
+ }
+
+ /**
+ * Example:
+ * ```php
+ * $client->doc->manifest(
+ * 'docId',
+ * 'layerName',
+ * new ManifestDocRequest([]),
+ * );
+ * ```
+ *
+ * @param string $docId
+ * @param string $layerName
+ * @param ManifestDocRequest $request
+ * @param ?array{
+ * baseUrl?: string,
+ * maxRetries?: int,
+ * timeout?: float,
+ * headers?: array,
+ * queryParameters?: array,
+ * bodyProperties?: array,
+ * } $options
+ * @return ?DocManifest200Response
+ * @throws CloudpdfException
+ * @throws CloudpdfApiException
+ */
+ public function manifest(string $docId, string $layerName, ManifestDocRequest $request = new ManifestDocRequest(), ?array $options = null): ?DocManifest200Response
+ {
+ $options = array_merge($this->options, $options ?? []);
+ $headers = [];
+ if ($request->documentPassword != null) {
+ $headers['X-Document-Password'] = $request->documentPassword;
+ }
+ try {
+ $response = $this->client->sendRequest(
+ new JsonApiRequest(
+ baseUrl: $options['baseUrl'] ?? $this->client->options['baseUrl'] ?? '',
+ path: "v1/docs/{$docId}/layers/{$layerName}/manifest",
+ method: HttpMethod::GET,
+ headers: $headers,
+ ),
+ $options,
+ );
+ $statusCode = $response->getStatusCode();
+ if ($statusCode >= 200 && $statusCode < 400) {
+ $json = $response->getBody()->getContents();
+ if (empty($json)) {
+ return null;
+ }
+ return DocManifest200Response::fromJson($json);
+ }
+ } catch (JsonException $e) {
+ throw new CloudpdfException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e);
+ } catch (ClientExceptionInterface $e) {
+ throw new CloudpdfException(message: $e->getMessage(), previous: $e);
+ }
+ throw new CloudpdfApiException(
+ message: 'API request failed',
+ statusCode: $statusCode,
+ body: $response->getBody()->getContents(),
+ );
+ }
+
+ /**
+ * Render parameters (viewport, format) pass as flat dotted query keys, e.g. `?viewport.kind=width&viewport.width=800`; the full grammar is documented with the viewer.
+ *
+ * Example:
+ * ```php
+ * $client->doc->render(
+ * 'docId',
+ * 'layerName',
+ * 1,
+ * new RenderDocRequest([]),
+ * );
+ * ```
+ *
+ * @param string $docId
+ * @param string $layerName
+ * @param int $pon
+ * @param RenderDocRequest $request
+ * @param ?array{
+ * baseUrl?: string,
+ * maxRetries?: int,
+ * timeout?: float,
+ * headers?: array,
+ * queryParameters?: array,
+ * bodyProperties?: array,
+ * } $options
+ * @return string
+ * @throws CloudpdfException
+ * @throws CloudpdfApiException
+ */
+ public function render(string $docId, string $layerName, int $pon, RenderDocRequest $request = new RenderDocRequest(), ?array $options = null): string
+ {
+ $options = array_merge($this->options, $options ?? []);
+ $headers = [];
+ if ($request->documentPassword != null) {
+ $headers['X-Document-Password'] = $request->documentPassword;
+ }
+ try {
+ $response = $this->client->sendRequest(
+ new JsonApiRequest(
+ baseUrl: $options['baseUrl'] ?? $this->client->options['baseUrl'] ?? '',
+ path: "v1/docs/{$docId}/layers/{$layerName}/render/pages/{$pon}/data",
+ method: HttpMethod::GET,
+ headers: $headers,
+ ),
+ $options,
+ );
+ $statusCode = $response->getStatusCode();
+ if ($statusCode >= 200 && $statusCode < 400) {
+ return $response->getBody()->getContents();
+ }
+ } catch (ClientExceptionInterface $e) {
+ throw new CloudpdfException(message: $e->getMessage(), previous: $e);
+ }
+ throw new CloudpdfApiException(
+ message: 'API request failed',
+ statusCode: $statusCode,
+ body: $response->getBody()->getContents(),
+ );
+ }
+
+ /**
+ * Example:
+ * ```php
+ * $client->doc->text(
+ * 'docId',
+ * 'layerName',
+ * 1,
+ * new TextDocRequest([]),
+ * );
+ * ```
+ *
+ * @param string $docId
+ * @param string $layerName
+ * @param int $pon
+ * @param TextDocRequest $request
+ * @param ?array{
+ * baseUrl?: string,
+ * maxRetries?: int,
+ * timeout?: float,
+ * headers?: array,
+ * queryParameters?: array,
+ * bodyProperties?: array,
+ * } $options
+ * @return ?DocText200Response
+ * @throws CloudpdfException
+ * @throws CloudpdfApiException
+ */
+ public function text(string $docId, string $layerName, int $pon, TextDocRequest $request = new TextDocRequest(), ?array $options = null): ?DocText200Response
+ {
+ $options = array_merge($this->options, $options ?? []);
+ $headers = [];
+ if ($request->documentPassword != null) {
+ $headers['X-Document-Password'] = $request->documentPassword;
+ }
+ try {
+ $response = $this->client->sendRequest(
+ new JsonApiRequest(
+ baseUrl: $options['baseUrl'] ?? $this->client->options['baseUrl'] ?? '',
+ path: "v1/docs/{$docId}/layers/{$layerName}/text/pages/{$pon}/data",
+ method: HttpMethod::GET,
+ headers: $headers,
+ ),
+ $options,
+ );
+ $statusCode = $response->getStatusCode();
+ if ($statusCode >= 200 && $statusCode < 400) {
+ $json = $response->getBody()->getContents();
+ if (empty($json)) {
+ return null;
+ }
+ return DocText200Response::fromJson($json);
+ }
+ } catch (JsonException $e) {
+ throw new CloudpdfException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e);
+ } catch (ClientExceptionInterface $e) {
+ throw new CloudpdfException(message: $e->getMessage(), previous: $e);
+ }
+ throw new CloudpdfApiException(
+ message: 'API request failed',
+ statusCode: $statusCode,
+ body: $response->getBody()->getContents(),
+ );
+ }
+}
diff --git a/src/Doc/Forms/FormsClient.php b/src/Doc/Forms/FormsClient.php
new file mode 100644
index 0000000..c709455
--- /dev/null
+++ b/src/Doc/Forms/FormsClient.php
@@ -0,0 +1,381 @@
+,
+ * } $options @phpstan-ignore-next-line Property is used in endpoint methods via HttpEndpointGenerator
+ */
+ private array $options;
+
+ /**
+ * @var RawClient $client
+ */
+ private RawClient $client;
+
+ /**
+ * @param RawClient $client
+ * @param ?array{
+ * baseUrl?: string,
+ * client?: ClientInterface,
+ * maxRetries?: int,
+ * timeout?: float,
+ * headers?: array,
+ * } $options
+ */
+ public function __construct(
+ RawClient $client,
+ ?array $options = null,
+ ) {
+ $this->client = $client;
+ $this->options = $options ?? [];
+ }
+
+ /**
+ * Example:
+ * ```php
+ * $client->doc->forms->get(
+ * 'docId',
+ * 'layerName',
+ * new GetFormsRequest([]),
+ * );
+ * ```
+ *
+ * @param string $docId
+ * @param string $layerName
+ * @param GetFormsRequest $request
+ * @param ?array{
+ * baseUrl?: string,
+ * maxRetries?: int,
+ * timeout?: float,
+ * headers?: array,
+ * queryParameters?: array,
+ * bodyProperties?: array,
+ * } $options
+ * @return ?DocFormsGet200Response
+ * @throws CloudpdfException
+ * @throws CloudpdfApiException
+ */
+ public function get(string $docId, string $layerName, GetFormsRequest $request = new GetFormsRequest(), ?array $options = null): ?DocFormsGet200Response
+ {
+ $options = array_merge($this->options, $options ?? []);
+ $headers = [];
+ if ($request->documentPassword != null) {
+ $headers['X-Document-Password'] = $request->documentPassword;
+ }
+ try {
+ $response = $this->client->sendRequest(
+ new JsonApiRequest(
+ baseUrl: $options['baseUrl'] ?? $this->client->options['baseUrl'] ?? '',
+ path: "v1/docs/{$docId}/layers/{$layerName}/form",
+ method: HttpMethod::GET,
+ headers: $headers,
+ ),
+ $options,
+ );
+ $statusCode = $response->getStatusCode();
+ if ($statusCode >= 200 && $statusCode < 400) {
+ $json = $response->getBody()->getContents();
+ if (empty($json)) {
+ return null;
+ }
+ return DocFormsGet200Response::fromJson($json);
+ }
+ } catch (JsonException $e) {
+ throw new CloudpdfException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e);
+ } catch (ClientExceptionInterface $e) {
+ throw new CloudpdfException(message: $e->getMessage(), previous: $e);
+ }
+ throw new CloudpdfApiException(
+ message: 'API request failed',
+ statusCode: $statusCode,
+ body: $response->getBody()->getContents(),
+ );
+ }
+
+ /**
+ * Example:
+ * ```php
+ * $client->doc->forms->exportData(
+ * 'docId',
+ * 'layerName',
+ * new ExportDataFormsRequest([]),
+ * );
+ * ```
+ *
+ * @param string $docId
+ * @param string $layerName
+ * @param ExportDataFormsRequest $request
+ * @param ?array{
+ * baseUrl?: string,
+ * maxRetries?: int,
+ * timeout?: float,
+ * headers?: array,
+ * queryParameters?: array,
+ * bodyProperties?: array,
+ * } $options
+ * @return string
+ * @throws CloudpdfException
+ * @throws CloudpdfApiException
+ */
+ public function exportData(string $docId, string $layerName, ExportDataFormsRequest $request = new ExportDataFormsRequest(), ?array $options = null): string
+ {
+ $options = array_merge($this->options, $options ?? []);
+ $query = [];
+ if ($request->format != null) {
+ $query['format'] = $request->format;
+ }
+ $headers = [];
+ if ($request->documentPassword != null) {
+ $headers['X-Document-Password'] = $request->documentPassword;
+ }
+ try {
+ $response = $this->client->sendRequest(
+ new JsonApiRequest(
+ baseUrl: $options['baseUrl'] ?? $this->client->options['baseUrl'] ?? '',
+ path: "v1/docs/{$docId}/layers/{$layerName}/form/data",
+ method: HttpMethod::GET,
+ headers: $headers,
+ query: $query,
+ ),
+ $options,
+ );
+ $statusCode = $response->getStatusCode();
+ if ($statusCode >= 200 && $statusCode < 400) {
+ return $response->getBody()->getContents();
+ }
+ } catch (ClientExceptionInterface $e) {
+ throw new CloudpdfException(message: $e->getMessage(), previous: $e);
+ }
+ throw new CloudpdfApiException(
+ message: 'API request failed',
+ statusCode: $statusCode,
+ body: $response->getBody()->getContents(),
+ );
+ }
+
+ /**
+ * Example:
+ * ```php
+ * $client->doc->forms->importData(
+ * 'docId',
+ * 'layerName',
+ * new ImportDataFormsRequest([
+ * 'body' => [
+ * 'key' => "value",
+ * ],
+ * ]),
+ * );
+ * ```
+ *
+ * @param string $docId
+ * @param string $layerName
+ * @param ImportDataFormsRequest $request
+ * @param ?array{
+ * baseUrl?: string,
+ * maxRetries?: int,
+ * timeout?: float,
+ * headers?: array,
+ * queryParameters?: array,
+ * bodyProperties?: array,
+ * } $options
+ * @return ?DocFormsImportData200Response
+ * @throws CloudpdfException
+ * @throws CloudpdfApiException
+ */
+ public function importData(string $docId, string $layerName, ImportDataFormsRequest $request, ?array $options = null): ?DocFormsImportData200Response
+ {
+ $options = array_merge($this->options, $options ?? []);
+ $headers = [];
+ if ($request->documentPassword != null) {
+ $headers['X-Document-Password'] = $request->documentPassword;
+ }
+ try {
+ $response = $this->client->sendRequest(
+ new JsonApiRequest(
+ baseUrl: $options['baseUrl'] ?? $this->client->options['baseUrl'] ?? '',
+ path: "v1/docs/{$docId}/layers/{$layerName}/form/data",
+ method: HttpMethod::POST,
+ headers: $headers,
+ body: $request->body,
+ ),
+ $options,
+ );
+ $statusCode = $response->getStatusCode();
+ if ($statusCode >= 200 && $statusCode < 400) {
+ $json = $response->getBody()->getContents();
+ if (empty($json)) {
+ return null;
+ }
+ return DocFormsImportData200Response::fromJson($json);
+ }
+ } catch (JsonException $e) {
+ throw new CloudpdfException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e);
+ } catch (ClientExceptionInterface $e) {
+ throw new CloudpdfException(message: $e->getMessage(), previous: $e);
+ }
+ throw new CloudpdfApiException(
+ message: 'API request failed',
+ statusCode: $statusCode,
+ body: $response->getBody()->getContents(),
+ );
+ }
+
+ /**
+ * Example:
+ * ```php
+ * $client->doc->forms->reset(
+ * 'docId',
+ * 'layerName',
+ * 'fieldKey',
+ * new ResetFormsRequest([]),
+ * );
+ * ```
+ *
+ * @param string $docId
+ * @param string $layerName
+ * @param string $fieldKey
+ * @param ResetFormsRequest $request
+ * @param ?array{
+ * baseUrl?: string,
+ * maxRetries?: int,
+ * timeout?: float,
+ * headers?: array,
+ * queryParameters?: array,
+ * bodyProperties?: array,
+ * } $options
+ * @return ?DocFormsReset200Response
+ * @throws CloudpdfException
+ * @throws CloudpdfApiException
+ */
+ public function reset(string $docId, string $layerName, string $fieldKey, ResetFormsRequest $request = new ResetFormsRequest(), ?array $options = null): ?DocFormsReset200Response
+ {
+ $options = array_merge($this->options, $options ?? []);
+ $headers = [];
+ if ($request->documentPassword != null) {
+ $headers['X-Document-Password'] = $request->documentPassword;
+ }
+ try {
+ $response = $this->client->sendRequest(
+ new JsonApiRequest(
+ baseUrl: $options['baseUrl'] ?? $this->client->options['baseUrl'] ?? '',
+ path: "v1/docs/{$docId}/layers/{$layerName}/form/fields/{$fieldKey}/reset",
+ method: HttpMethod::POST,
+ headers: $headers,
+ ),
+ $options,
+ );
+ $statusCode = $response->getStatusCode();
+ if ($statusCode >= 200 && $statusCode < 400) {
+ $json = $response->getBody()->getContents();
+ if (empty($json)) {
+ return null;
+ }
+ return DocFormsReset200Response::fromJson($json);
+ }
+ } catch (JsonException $e) {
+ throw new CloudpdfException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e);
+ } catch (ClientExceptionInterface $e) {
+ throw new CloudpdfException(message: $e->getMessage(), previous: $e);
+ }
+ throw new CloudpdfApiException(
+ message: 'API request failed',
+ statusCode: $statusCode,
+ body: $response->getBody()->getContents(),
+ );
+ }
+
+ /**
+ * Example:
+ * ```php
+ * $client->doc->forms->setValue(
+ * 'docId',
+ * 'layerName',
+ * 'fieldKey',
+ * new SetValueFormsRequest([
+ * 'body' => [
+ * 'key' => "value",
+ * ],
+ * ]),
+ * );
+ * ```
+ *
+ * @param string $docId
+ * @param string $layerName
+ * @param string $fieldKey
+ * @param SetValueFormsRequest $request
+ * @param ?array{
+ * baseUrl?: string,
+ * maxRetries?: int,
+ * timeout?: float,
+ * headers?: array,
+ * queryParameters?: array,
+ * bodyProperties?: array,
+ * } $options
+ * @return ?DocFormsSetValue200Response
+ * @throws CloudpdfException
+ * @throws CloudpdfApiException
+ */
+ public function setValue(string $docId, string $layerName, string $fieldKey, SetValueFormsRequest $request, ?array $options = null): ?DocFormsSetValue200Response
+ {
+ $options = array_merge($this->options, $options ?? []);
+ $headers = [];
+ if ($request->documentPassword != null) {
+ $headers['X-Document-Password'] = $request->documentPassword;
+ }
+ try {
+ $response = $this->client->sendRequest(
+ new JsonApiRequest(
+ baseUrl: $options['baseUrl'] ?? $this->client->options['baseUrl'] ?? '',
+ path: "v1/docs/{$docId}/layers/{$layerName}/form/fields/{$fieldKey}/value",
+ method: HttpMethod::POST,
+ headers: $headers,
+ body: $request->body,
+ ),
+ $options,
+ );
+ $statusCode = $response->getStatusCode();
+ if ($statusCode >= 200 && $statusCode < 400) {
+ $json = $response->getBody()->getContents();
+ if (empty($json)) {
+ return null;
+ }
+ return DocFormsSetValue200Response::fromJson($json);
+ }
+ } catch (JsonException $e) {
+ throw new CloudpdfException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e);
+ } catch (ClientExceptionInterface $e) {
+ throw new CloudpdfException(message: $e->getMessage(), previous: $e);
+ }
+ throw new CloudpdfApiException(
+ message: 'API request failed',
+ statusCode: $statusCode,
+ body: $response->getBody()->getContents(),
+ );
+ }
+}
diff --git a/src/Doc/Forms/Requests/ExportDataFormsRequest.php b/src/Doc/Forms/Requests/ExportDataFormsRequest.php
new file mode 100644
index 0000000..17aee61
--- /dev/null
+++ b/src/Doc/Forms/Requests/ExportDataFormsRequest.php
@@ -0,0 +1,32 @@
+ $format
+ */
+ public ?string $format;
+
+ /**
+ * @var ?string $documentPassword Base64-encoded password for an encrypted document. Valid only with the API token (403 anywhere else). An encrypted document answers 422 DocPasswordRequired when the header is absent. Viewer doc JWTs use the SDK password-session flow instead.
+ */
+ public ?string $documentPassword;
+
+ /**
+ * @param array{
+ * format?: ?value-of,
+ * documentPassword?: ?string,
+ * } $values
+ */
+ public function __construct(
+ array $values = [],
+ ) {
+ $this->format = $values['format'] ?? null;
+ $this->documentPassword = $values['documentPassword'] ?? null;
+ }
+}
diff --git a/src/Doc/Forms/Requests/GetFormsRequest.php b/src/Doc/Forms/Requests/GetFormsRequest.php
new file mode 100644
index 0000000..58a29a5
--- /dev/null
+++ b/src/Doc/Forms/Requests/GetFormsRequest.php
@@ -0,0 +1,24 @@
+documentPassword = $values['documentPassword'] ?? null;
+ }
+}
diff --git a/src/Doc/Forms/Requests/ImportDataFormsRequest.php b/src/Doc/Forms/Requests/ImportDataFormsRequest.php
new file mode 100644
index 0000000..028cc80
--- /dev/null
+++ b/src/Doc/Forms/Requests/ImportDataFormsRequest.php
@@ -0,0 +1,31 @@
+ $body
+ */
+ public array $body;
+
+ /**
+ * @param array{
+ * body: array,
+ * documentPassword?: ?string,
+ * } $values
+ */
+ public function __construct(
+ array $values,
+ ) {
+ $this->documentPassword = $values['documentPassword'] ?? null;
+ $this->body = $values['body'];
+ }
+}
diff --git a/src/Doc/Forms/Requests/ResetFormsRequest.php b/src/Doc/Forms/Requests/ResetFormsRequest.php
new file mode 100644
index 0000000..e999758
--- /dev/null
+++ b/src/Doc/Forms/Requests/ResetFormsRequest.php
@@ -0,0 +1,24 @@
+documentPassword = $values['documentPassword'] ?? null;
+ }
+}
diff --git a/src/Doc/Forms/Requests/SetValueFormsRequest.php b/src/Doc/Forms/Requests/SetValueFormsRequest.php
new file mode 100644
index 0000000..3b16526
--- /dev/null
+++ b/src/Doc/Forms/Requests/SetValueFormsRequest.php
@@ -0,0 +1,31 @@
+ $body
+ */
+ public array $body;
+
+ /**
+ * @param array{
+ * body: array,
+ * documentPassword?: ?string,
+ * } $values
+ */
+ public function __construct(
+ array $values,
+ ) {
+ $this->documentPassword = $values['documentPassword'] ?? null;
+ $this->body = $values['body'];
+ }
+}
diff --git a/src/Doc/Forms/Types/ExportDataFormsRequestFormat.php b/src/Doc/Forms/Types/ExportDataFormsRequestFormat.php
new file mode 100644
index 0000000..1ffb18f
--- /dev/null
+++ b/src/Doc/Forms/Types/ExportDataFormsRequestFormat.php
@@ -0,0 +1,9 @@
+,
+ * } $options @phpstan-ignore-next-line Property is used in endpoint methods via HttpEndpointGenerator
+ */
+ private array $options;
+
+ /**
+ * @var RawClient $client
+ */
+ private RawClient $client;
+
+ /**
+ * @param RawClient $client
+ * @param ?array{
+ * baseUrl?: string,
+ * client?: ClientInterface,
+ * maxRetries?: int,
+ * timeout?: float,
+ * headers?: array,
+ * } $options
+ */
+ public function __construct(
+ RawClient $client,
+ ?array $options = null,
+ ) {
+ $this->client = $client;
+ $this->options = $options ?? [];
+ }
+
+ /**
+ * Example:
+ * ```php
+ * $client->doc->metadata->get(
+ * 'docId',
+ * 'layerName',
+ * new GetMetadataRequest([]),
+ * );
+ * ```
+ *
+ * @param string $docId
+ * @param string $layerName
+ * @param GetMetadataRequest $request
+ * @param ?array{
+ * baseUrl?: string,
+ * maxRetries?: int,
+ * timeout?: float,
+ * headers?: array,
+ * queryParameters?: array,
+ * bodyProperties?: array,
+ * } $options
+ * @return ?DocMetadataGet200Response
+ * @throws CloudpdfException
+ * @throws CloudpdfApiException
+ */
+ public function get(string $docId, string $layerName, GetMetadataRequest $request = new GetMetadataRequest(), ?array $options = null): ?DocMetadataGet200Response
+ {
+ $options = array_merge($this->options, $options ?? []);
+ $headers = [];
+ if ($request->documentPassword != null) {
+ $headers['X-Document-Password'] = $request->documentPassword;
+ }
+ try {
+ $response = $this->client->sendRequest(
+ new JsonApiRequest(
+ baseUrl: $options['baseUrl'] ?? $this->client->options['baseUrl'] ?? '',
+ path: "v1/docs/{$docId}/layers/{$layerName}/metadata",
+ method: HttpMethod::GET,
+ headers: $headers,
+ ),
+ $options,
+ );
+ $statusCode = $response->getStatusCode();
+ if ($statusCode >= 200 && $statusCode < 400) {
+ $json = $response->getBody()->getContents();
+ if (empty($json)) {
+ return null;
+ }
+ return DocMetadataGet200Response::fromJson($json);
+ }
+ } catch (JsonException $e) {
+ throw new CloudpdfException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e);
+ } catch (ClientExceptionInterface $e) {
+ throw new CloudpdfException(message: $e->getMessage(), previous: $e);
+ }
+ throw new CloudpdfApiException(
+ message: 'API request failed',
+ statusCode: $statusCode,
+ body: $response->getBody()->getContents(),
+ );
+ }
+}
diff --git a/src/Doc/Metadata/Requests/GetMetadataRequest.php b/src/Doc/Metadata/Requests/GetMetadataRequest.php
new file mode 100644
index 0000000..0c4ea91
--- /dev/null
+++ b/src/Doc/Metadata/Requests/GetMetadataRequest.php
@@ -0,0 +1,24 @@
+documentPassword = $values['documentPassword'] ?? null;
+ }
+}
diff --git a/src/Doc/Pages/PagesClient.php b/src/Doc/Pages/PagesClient.php
new file mode 100644
index 0000000..a1d0de3
--- /dev/null
+++ b/src/Doc/Pages/PagesClient.php
@@ -0,0 +1,325 @@
+,
+ * } $options @phpstan-ignore-next-line Property is used in endpoint methods via HttpEndpointGenerator
+ */
+ private array $options;
+
+ /**
+ * @var RawClient $client
+ */
+ private RawClient $client;
+
+ /**
+ * @param RawClient $client
+ * @param ?array{
+ * baseUrl?: string,
+ * client?: ClientInterface,
+ * maxRetries?: int,
+ * timeout?: float,
+ * headers?: array,
+ * } $options
+ */
+ public function __construct(
+ RawClient $client,
+ ?array $options = null,
+ ) {
+ $this->client = $client;
+ $this->options = $options ?? [];
+ }
+
+ /**
+ * Example:
+ * ```php
+ * $client->doc->pages->delete(
+ * 'docId',
+ * 'layerName',
+ * new DeletePagesRequest([
+ * 'body' => [
+ * 'key' => "value",
+ * ],
+ * ]),
+ * );
+ * ```
+ *
+ * @param string $docId
+ * @param string $layerName
+ * @param DeletePagesRequest $request
+ * @param ?array{
+ * baseUrl?: string,
+ * maxRetries?: int,
+ * timeout?: float,
+ * headers?: array,
+ * queryParameters?: array,
+ * bodyProperties?: array,
+ * } $options
+ * @return ?DocPagesDelete200Response
+ * @throws CloudpdfException
+ * @throws CloudpdfApiException
+ */
+ public function delete(string $docId, string $layerName, DeletePagesRequest $request, ?array $options = null): ?DocPagesDelete200Response
+ {
+ $options = array_merge($this->options, $options ?? []);
+ $headers = [];
+ if ($request->documentPassword != null) {
+ $headers['X-Document-Password'] = $request->documentPassword;
+ }
+ try {
+ $response = $this->client->sendRequest(
+ new JsonApiRequest(
+ baseUrl: $options['baseUrl'] ?? $this->client->options['baseUrl'] ?? '',
+ path: "v1/docs/{$docId}/layers/{$layerName}/pages/delete",
+ method: HttpMethod::POST,
+ headers: $headers,
+ body: $request->body,
+ ),
+ $options,
+ );
+ $statusCode = $response->getStatusCode();
+ if ($statusCode >= 200 && $statusCode < 400) {
+ $json = $response->getBody()->getContents();
+ if (empty($json)) {
+ return null;
+ }
+ return DocPagesDelete200Response::fromJson($json);
+ }
+ } catch (JsonException $e) {
+ throw new CloudpdfException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e);
+ } catch (ClientExceptionInterface $e) {
+ throw new CloudpdfException(message: $e->getMessage(), previous: $e);
+ }
+ throw new CloudpdfApiException(
+ message: 'API request failed',
+ statusCode: $statusCode,
+ body: $response->getBody()->getContents(),
+ );
+ }
+
+ /**
+ * Example:
+ * ```php
+ * $client->doc->pages->flatten(
+ * 'docId',
+ * 'layerName',
+ * new FlattenPagesRequest([
+ * 'body' => [
+ * 'key' => "value",
+ * ],
+ * ]),
+ * );
+ * ```
+ *
+ * @param string $docId
+ * @param string $layerName
+ * @param FlattenPagesRequest $request
+ * @param ?array{
+ * baseUrl?: string,
+ * maxRetries?: int,
+ * timeout?: float,
+ * headers?: array,
+ * queryParameters?: array,
+ * bodyProperties?: array,
+ * } $options
+ * @return ?DocPagesFlatten200Response
+ * @throws CloudpdfException
+ * @throws CloudpdfApiException
+ */
+ public function flatten(string $docId, string $layerName, FlattenPagesRequest $request, ?array $options = null): ?DocPagesFlatten200Response
+ {
+ $options = array_merge($this->options, $options ?? []);
+ $headers = [];
+ if ($request->documentPassword != null) {
+ $headers['X-Document-Password'] = $request->documentPassword;
+ }
+ try {
+ $response = $this->client->sendRequest(
+ new JsonApiRequest(
+ baseUrl: $options['baseUrl'] ?? $this->client->options['baseUrl'] ?? '',
+ path: "v1/docs/{$docId}/layers/{$layerName}/pages/flatten",
+ method: HttpMethod::POST,
+ headers: $headers,
+ body: $request->body,
+ ),
+ $options,
+ );
+ $statusCode = $response->getStatusCode();
+ if ($statusCode >= 200 && $statusCode < 400) {
+ $json = $response->getBody()->getContents();
+ if (empty($json)) {
+ return null;
+ }
+ return DocPagesFlatten200Response::fromJson($json);
+ }
+ } catch (JsonException $e) {
+ throw new CloudpdfException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e);
+ } catch (ClientExceptionInterface $e) {
+ throw new CloudpdfException(message: $e->getMessage(), previous: $e);
+ }
+ throw new CloudpdfApiException(
+ message: 'API request failed',
+ statusCode: $statusCode,
+ body: $response->getBody()->getContents(),
+ );
+ }
+
+ /**
+ * Example:
+ * ```php
+ * $client->doc->pages->move(
+ * 'docId',
+ * 'layerName',
+ * new MovePagesRequest([
+ * 'body' => [
+ * 'key' => "value",
+ * ],
+ * ]),
+ * );
+ * ```
+ *
+ * @param string $docId
+ * @param string $layerName
+ * @param MovePagesRequest $request
+ * @param ?array{
+ * baseUrl?: string,
+ * maxRetries?: int,
+ * timeout?: float,
+ * headers?: array,
+ * queryParameters?: array,
+ * bodyProperties?: array,
+ * } $options
+ * @return ?DocPagesMove200Response
+ * @throws CloudpdfException
+ * @throws CloudpdfApiException
+ */
+ public function move(string $docId, string $layerName, MovePagesRequest $request, ?array $options = null): ?DocPagesMove200Response
+ {
+ $options = array_merge($this->options, $options ?? []);
+ $headers = [];
+ if ($request->documentPassword != null) {
+ $headers['X-Document-Password'] = $request->documentPassword;
+ }
+ try {
+ $response = $this->client->sendRequest(
+ new JsonApiRequest(
+ baseUrl: $options['baseUrl'] ?? $this->client->options['baseUrl'] ?? '',
+ path: "v1/docs/{$docId}/layers/{$layerName}/pages/move",
+ method: HttpMethod::POST,
+ headers: $headers,
+ body: $request->body,
+ ),
+ $options,
+ );
+ $statusCode = $response->getStatusCode();
+ if ($statusCode >= 200 && $statusCode < 400) {
+ $json = $response->getBody()->getContents();
+ if (empty($json)) {
+ return null;
+ }
+ return DocPagesMove200Response::fromJson($json);
+ }
+ } catch (JsonException $e) {
+ throw new CloudpdfException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e);
+ } catch (ClientExceptionInterface $e) {
+ throw new CloudpdfException(message: $e->getMessage(), previous: $e);
+ }
+ throw new CloudpdfApiException(
+ message: 'API request failed',
+ statusCode: $statusCode,
+ body: $response->getBody()->getContents(),
+ );
+ }
+
+ /**
+ * Example:
+ * ```php
+ * $client->doc->pages->rotate(
+ * 'docId',
+ * 'layerName',
+ * new RotatePagesRequest([
+ * 'body' => [
+ * 'key' => "value",
+ * ],
+ * ]),
+ * );
+ * ```
+ *
+ * @param string $docId
+ * @param string $layerName
+ * @param RotatePagesRequest $request
+ * @param ?array{
+ * baseUrl?: string,
+ * maxRetries?: int,
+ * timeout?: float,
+ * headers?: array,
+ * queryParameters?: array,
+ * bodyProperties?: array,
+ * } $options
+ * @return ?DocPagesRotate200Response
+ * @throws CloudpdfException
+ * @throws CloudpdfApiException
+ */
+ public function rotate(string $docId, string $layerName, RotatePagesRequest $request, ?array $options = null): ?DocPagesRotate200Response
+ {
+ $options = array_merge($this->options, $options ?? []);
+ $headers = [];
+ if ($request->documentPassword != null) {
+ $headers['X-Document-Password'] = $request->documentPassword;
+ }
+ try {
+ $response = $this->client->sendRequest(
+ new JsonApiRequest(
+ baseUrl: $options['baseUrl'] ?? $this->client->options['baseUrl'] ?? '',
+ path: "v1/docs/{$docId}/layers/{$layerName}/pages/rotate",
+ method: HttpMethod::POST,
+ headers: $headers,
+ body: $request->body,
+ ),
+ $options,
+ );
+ $statusCode = $response->getStatusCode();
+ if ($statusCode >= 200 && $statusCode < 400) {
+ $json = $response->getBody()->getContents();
+ if (empty($json)) {
+ return null;
+ }
+ return DocPagesRotate200Response::fromJson($json);
+ }
+ } catch (JsonException $e) {
+ throw new CloudpdfException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e);
+ } catch (ClientExceptionInterface $e) {
+ throw new CloudpdfException(message: $e->getMessage(), previous: $e);
+ }
+ throw new CloudpdfApiException(
+ message: 'API request failed',
+ statusCode: $statusCode,
+ body: $response->getBody()->getContents(),
+ );
+ }
+}
diff --git a/src/Doc/Pages/Requests/DeletePagesRequest.php b/src/Doc/Pages/Requests/DeletePagesRequest.php
new file mode 100644
index 0000000..8408913
--- /dev/null
+++ b/src/Doc/Pages/Requests/DeletePagesRequest.php
@@ -0,0 +1,31 @@
+ $body
+ */
+ public array $body;
+
+ /**
+ * @param array{
+ * body: array,
+ * documentPassword?: ?string,
+ * } $values
+ */
+ public function __construct(
+ array $values,
+ ) {
+ $this->documentPassword = $values['documentPassword'] ?? null;
+ $this->body = $values['body'];
+ }
+}
diff --git a/src/Doc/Pages/Requests/FlattenPagesRequest.php b/src/Doc/Pages/Requests/FlattenPagesRequest.php
new file mode 100644
index 0000000..b76725d
--- /dev/null
+++ b/src/Doc/Pages/Requests/FlattenPagesRequest.php
@@ -0,0 +1,31 @@
+ $body
+ */
+ public array $body;
+
+ /**
+ * @param array{
+ * body: array,
+ * documentPassword?: ?string,
+ * } $values
+ */
+ public function __construct(
+ array $values,
+ ) {
+ $this->documentPassword = $values['documentPassword'] ?? null;
+ $this->body = $values['body'];
+ }
+}
diff --git a/src/Doc/Pages/Requests/MovePagesRequest.php b/src/Doc/Pages/Requests/MovePagesRequest.php
new file mode 100644
index 0000000..38f505e
--- /dev/null
+++ b/src/Doc/Pages/Requests/MovePagesRequest.php
@@ -0,0 +1,31 @@
+ $body
+ */
+ public array $body;
+
+ /**
+ * @param array{
+ * body: array,
+ * documentPassword?: ?string,
+ * } $values
+ */
+ public function __construct(
+ array $values,
+ ) {
+ $this->documentPassword = $values['documentPassword'] ?? null;
+ $this->body = $values['body'];
+ }
+}
diff --git a/src/Doc/Pages/Requests/RotatePagesRequest.php b/src/Doc/Pages/Requests/RotatePagesRequest.php
new file mode 100644
index 0000000..879b968
--- /dev/null
+++ b/src/Doc/Pages/Requests/RotatePagesRequest.php
@@ -0,0 +1,31 @@
+ $body
+ */
+ public array $body;
+
+ /**
+ * @param array{
+ * body: array,
+ * documentPassword?: ?string,
+ * } $values
+ */
+ public function __construct(
+ array $values,
+ ) {
+ $this->documentPassword = $values['documentPassword'] ?? null;
+ $this->body = $values['body'];
+ }
+}
diff --git a/src/Doc/Redactions/RedactionsClient.php b/src/Doc/Redactions/RedactionsClient.php
new file mode 100644
index 0000000..c449ab6
--- /dev/null
+++ b/src/Doc/Redactions/RedactionsClient.php
@@ -0,0 +1,118 @@
+,
+ * } $options @phpstan-ignore-next-line Property is used in endpoint methods via HttpEndpointGenerator
+ */
+ private array $options;
+
+ /**
+ * @var RawClient $client
+ */
+ private RawClient $client;
+
+ /**
+ * @param RawClient $client
+ * @param ?array{
+ * baseUrl?: string,
+ * client?: ClientInterface,
+ * maxRetries?: int,
+ * timeout?: float,
+ * headers?: array,
+ * } $options
+ */
+ public function __construct(
+ RawClient $client,
+ ?array $options = null,
+ ) {
+ $this->client = $client;
+ $this->options = $options ?? [];
+ }
+
+ /**
+ * Example:
+ * ```php
+ * $client->doc->redactions->apply(
+ * 'docId',
+ * 'layerName',
+ * new ApplyRedactionsRequest([
+ * 'body' => [
+ * 'key' => "value",
+ * ],
+ * ]),
+ * );
+ * ```
+ *
+ * @param string $docId
+ * @param string $layerName
+ * @param ApplyRedactionsRequest $request
+ * @param ?array{
+ * baseUrl?: string,
+ * maxRetries?: int,
+ * timeout?: float,
+ * headers?: array,
+ * queryParameters?: array,
+ * bodyProperties?: array,
+ * } $options
+ * @return ?DocRedactionsApply200Response
+ * @throws CloudpdfException
+ * @throws CloudpdfApiException
+ */
+ public function apply(string $docId, string $layerName, ApplyRedactionsRequest $request, ?array $options = null): ?DocRedactionsApply200Response
+ {
+ $options = array_merge($this->options, $options ?? []);
+ $headers = [];
+ if ($request->documentPassword != null) {
+ $headers['X-Document-Password'] = $request->documentPassword;
+ }
+ try {
+ $response = $this->client->sendRequest(
+ new JsonApiRequest(
+ baseUrl: $options['baseUrl'] ?? $this->client->options['baseUrl'] ?? '',
+ path: "v1/docs/{$docId}/layers/{$layerName}/redactions/apply",
+ method: HttpMethod::POST,
+ headers: $headers,
+ body: $request->body,
+ ),
+ $options,
+ );
+ $statusCode = $response->getStatusCode();
+ if ($statusCode >= 200 && $statusCode < 400) {
+ $json = $response->getBody()->getContents();
+ if (empty($json)) {
+ return null;
+ }
+ return DocRedactionsApply200Response::fromJson($json);
+ }
+ } catch (JsonException $e) {
+ throw new CloudpdfException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e);
+ } catch (ClientExceptionInterface $e) {
+ throw new CloudpdfException(message: $e->getMessage(), previous: $e);
+ }
+ throw new CloudpdfApiException(
+ message: 'API request failed',
+ statusCode: $statusCode,
+ body: $response->getBody()->getContents(),
+ );
+ }
+}
diff --git a/src/Doc/Redactions/Requests/ApplyRedactionsRequest.php b/src/Doc/Redactions/Requests/ApplyRedactionsRequest.php
new file mode 100644
index 0000000..db93f40
--- /dev/null
+++ b/src/Doc/Redactions/Requests/ApplyRedactionsRequest.php
@@ -0,0 +1,31 @@
+ $body
+ */
+ public array $body;
+
+ /**
+ * @param array{
+ * body: array,
+ * documentPassword?: ?string,
+ * } $values
+ */
+ public function __construct(
+ array $values,
+ ) {
+ $this->documentPassword = $values['documentPassword'] ?? null;
+ $this->body = $values['body'];
+ }
+}
diff --git a/src/Doc/Requests/DownloadDocRequest.php b/src/Doc/Requests/DownloadDocRequest.php
new file mode 100644
index 0000000..342d809
--- /dev/null
+++ b/src/Doc/Requests/DownloadDocRequest.php
@@ -0,0 +1,24 @@
+documentPassword = $values['documentPassword'] ?? null;
+ }
+}
diff --git a/src/Doc/Requests/HeadDocRequest.php b/src/Doc/Requests/HeadDocRequest.php
new file mode 100644
index 0000000..85130e0
--- /dev/null
+++ b/src/Doc/Requests/HeadDocRequest.php
@@ -0,0 +1,24 @@
+documentPassword = $values['documentPassword'] ?? null;
+ }
+}
diff --git a/src/Doc/Requests/ManifestDocRequest.php b/src/Doc/Requests/ManifestDocRequest.php
new file mode 100644
index 0000000..3a3ee73
--- /dev/null
+++ b/src/Doc/Requests/ManifestDocRequest.php
@@ -0,0 +1,24 @@
+documentPassword = $values['documentPassword'] ?? null;
+ }
+}
diff --git a/src/Doc/Requests/RenderDocRequest.php b/src/Doc/Requests/RenderDocRequest.php
new file mode 100644
index 0000000..c08ba45
--- /dev/null
+++ b/src/Doc/Requests/RenderDocRequest.php
@@ -0,0 +1,24 @@
+documentPassword = $values['documentPassword'] ?? null;
+ }
+}
diff --git a/src/Doc/Requests/TextDocRequest.php b/src/Doc/Requests/TextDocRequest.php
new file mode 100644
index 0000000..5749359
--- /dev/null
+++ b/src/Doc/Requests/TextDocRequest.php
@@ -0,0 +1,24 @@
+documentPassword = $values['documentPassword'] ?? null;
+ }
+}
diff --git a/src/Documents/DocumentsClient.php b/src/Documents/DocumentsClient.php
new file mode 100644
index 0000000..bace738
--- /dev/null
+++ b/src/Documents/DocumentsClient.php
@@ -0,0 +1,490 @@
+,
+ * } $options @phpstan-ignore-next-line Property is used in endpoint methods via HttpEndpointGenerator
+ */
+ private array $options;
+
+ /**
+ * @var RawClient $client
+ */
+ private RawClient $client;
+
+ /**
+ * @param RawClient $client
+ * @param ?array{
+ * baseUrl?: string,
+ * client?: ClientInterface,
+ * maxRetries?: int,
+ * timeout?: float,
+ * headers?: array,
+ * } $options
+ */
+ public function __construct(
+ RawClient $client,
+ ?array $options = null,
+ ) {
+ $this->client = $client;
+ $this->options = $options ?? [];
+ }
+
+ /**
+ * Example:
+ * ```php
+ * $client->documents->list(
+ * 'tenantId',
+ * new ListDocumentsRequest([]),
+ * );
+ * ```
+ *
+ * @param string $tenantId
+ * @param ListDocumentsRequest $request
+ * @param ?array{
+ * baseUrl?: string,
+ * maxRetries?: int,
+ * timeout?: float,
+ * headers?: array,
+ * queryParameters?: array,
+ * bodyProperties?: array,
+ * } $options
+ * @return ?DocumentsList200Response
+ * @throws CloudpdfException
+ * @throws CloudpdfApiException
+ */
+ public function list(string $tenantId, ListDocumentsRequest $request = new ListDocumentsRequest(), ?array $options = null): ?DocumentsList200Response
+ {
+ $options = array_merge($this->options, $options ?? []);
+ $query = [];
+ if ($request->limit != null) {
+ $query['limit'] = $request->limit;
+ }
+ if ($request->cursor != null) {
+ $query['cursor'] = $request->cursor;
+ }
+ if ($request->state != null) {
+ $query['state'] = $request->state;
+ }
+ try {
+ $response = $this->client->sendRequest(
+ new JsonApiRequest(
+ baseUrl: $options['baseUrl'] ?? $this->client->options['baseUrl'] ?? '',
+ path: "v1/tenants/{$tenantId}/documents",
+ method: HttpMethod::GET,
+ query: $query,
+ ),
+ $options,
+ );
+ $statusCode = $response->getStatusCode();
+ if ($statusCode >= 200 && $statusCode < 400) {
+ $json = $response->getBody()->getContents();
+ if (empty($json)) {
+ return null;
+ }
+ return DocumentsList200Response::fromJson($json);
+ }
+ } catch (JsonException $e) {
+ throw new CloudpdfException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e);
+ } catch (ClientExceptionInterface $e) {
+ throw new CloudpdfException(message: $e->getMessage(), previous: $e);
+ }
+ throw new CloudpdfApiException(
+ message: 'API request failed',
+ statusCode: $statusCode,
+ body: $response->getBody()->getContents(),
+ );
+ }
+
+ /**
+ * Example:
+ * ```php
+ * $client->documents->get(
+ * 'tenantId',
+ * 'id',
+ * );
+ * ```
+ *
+ * @param string $tenantId
+ * @param string $id
+ * @param ?array{
+ * baseUrl?: string,
+ * maxRetries?: int,
+ * timeout?: float,
+ * headers?: array,
+ * queryParameters?: array,
+ * bodyProperties?: array,
+ * } $options
+ * @return ?DocumentsGet200Response
+ * @throws CloudpdfException
+ * @throws CloudpdfApiException
+ */
+ public function get(string $tenantId, string $id, ?array $options = null): ?DocumentsGet200Response
+ {
+ $options = array_merge($this->options, $options ?? []);
+ try {
+ $response = $this->client->sendRequest(
+ new JsonApiRequest(
+ baseUrl: $options['baseUrl'] ?? $this->client->options['baseUrl'] ?? '',
+ path: "v1/tenants/{$tenantId}/documents/{$id}",
+ method: HttpMethod::GET,
+ ),
+ $options,
+ );
+ $statusCode = $response->getStatusCode();
+ if ($statusCode >= 200 && $statusCode < 400) {
+ $json = $response->getBody()->getContents();
+ if (empty($json)) {
+ return null;
+ }
+ return DocumentsGet200Response::fromJson($json);
+ }
+ } catch (JsonException $e) {
+ throw new CloudpdfException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e);
+ } catch (ClientExceptionInterface $e) {
+ throw new CloudpdfException(message: $e->getMessage(), previous: $e);
+ }
+ throw new CloudpdfApiException(
+ message: 'API request failed',
+ statusCode: $statusCode,
+ body: $response->getBody()->getContents(),
+ );
+ }
+
+ /**
+ * Example:
+ * ```php
+ * $client->documents->delete(
+ * 'tenantId',
+ * 'id',
+ * );
+ * ```
+ *
+ * @param string $tenantId
+ * @param string $id
+ * @param ?array{
+ * baseUrl?: string,
+ * maxRetries?: int,
+ * timeout?: float,
+ * headers?: array,
+ * queryParameters?: array,
+ * bodyProperties?: array,
+ * } $options
+ * @throws CloudpdfException
+ * @throws CloudpdfApiException
+ */
+ public function delete(string $tenantId, string $id, ?array $options = null): void
+ {
+ $options = array_merge($this->options, $options ?? []);
+ try {
+ $response = $this->client->sendRequest(
+ new JsonApiRequest(
+ baseUrl: $options['baseUrl'] ?? $this->client->options['baseUrl'] ?? '',
+ path: "v1/tenants/{$tenantId}/documents/{$id}",
+ method: HttpMethod::DELETE,
+ ),
+ $options,
+ );
+ $statusCode = $response->getStatusCode();
+ if ($statusCode >= 200 && $statusCode < 400) {
+ return;
+ }
+ } catch (ClientExceptionInterface $e) {
+ throw new CloudpdfException(message: $e->getMessage(), previous: $e);
+ }
+ throw new CloudpdfApiException(
+ message: 'API request failed',
+ statusCode: $statusCode,
+ body: $response->getBody()->getContents(),
+ );
+ }
+
+ /**
+ * Example:
+ * ```php
+ * $client->documents->commit(
+ * 'tenantId',
+ * 'id',
+ * new DocumentsCommitRequest([
+ * 'sha256' => 'sha256',
+ * ]),
+ * );
+ * ```
+ *
+ * @param string $tenantId
+ * @param string $id
+ * @param DocumentsCommitRequest $request
+ * @param ?array{
+ * baseUrl?: string,
+ * maxRetries?: int,
+ * timeout?: float,
+ * headers?: array,
+ * queryParameters?: array,
+ * bodyProperties?: array,
+ * } $options
+ * @return ?DocumentsCommit200Response
+ * @throws CloudpdfException
+ * @throws CloudpdfApiException
+ */
+ public function commit(string $tenantId, string $id, DocumentsCommitRequest $request, ?array $options = null): ?DocumentsCommit200Response
+ {
+ $options = array_merge($this->options, $options ?? []);
+ try {
+ $response = $this->client->sendRequest(
+ new JsonApiRequest(
+ baseUrl: $options['baseUrl'] ?? $this->client->options['baseUrl'] ?? '',
+ path: "v1/tenants/{$tenantId}/documents/{$id}/commit",
+ method: HttpMethod::POST,
+ body: $request,
+ ),
+ $options,
+ );
+ $statusCode = $response->getStatusCode();
+ if ($statusCode >= 200 && $statusCode < 400) {
+ $json = $response->getBody()->getContents();
+ if (empty($json)) {
+ return null;
+ }
+ return DocumentsCommit200Response::fromJson($json);
+ }
+ } catch (JsonException $e) {
+ throw new CloudpdfException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e);
+ } catch (ClientExceptionInterface $e) {
+ throw new CloudpdfException(message: $e->getMessage(), previous: $e);
+ }
+ throw new CloudpdfApiException(
+ message: 'API request failed',
+ statusCode: $statusCode,
+ body: $response->getBody()->getContents(),
+ );
+ }
+
+ /**
+ * Example:
+ * ```php
+ * $client->documents->download(
+ * 'tenantId',
+ * 'id',
+ * );
+ * ```
+ *
+ * @param string $tenantId
+ * @param string $id
+ * @param ?array{
+ * baseUrl?: string,
+ * maxRetries?: int,
+ * timeout?: float,
+ * headers?: array,
+ * queryParameters?: array,
+ * bodyProperties?: array,
+ * } $options
+ * @return string
+ * @throws CloudpdfException
+ * @throws CloudpdfApiException
+ */
+ public function download(string $tenantId, string $id, ?array $options = null): string
+ {
+ $options = array_merge($this->options, $options ?? []);
+ try {
+ $response = $this->client->sendRequest(
+ new JsonApiRequest(
+ baseUrl: $options['baseUrl'] ?? $this->client->options['baseUrl'] ?? '',
+ path: "v1/tenants/{$tenantId}/documents/{$id}/download",
+ method: HttpMethod::GET,
+ ),
+ $options,
+ );
+ $statusCode = $response->getStatusCode();
+ if ($statusCode >= 200 && $statusCode < 400) {
+ return $response->getBody()->getContents();
+ }
+ } catch (ClientExceptionInterface $e) {
+ throw new CloudpdfException(message: $e->getMessage(), previous: $e);
+ }
+ throw new CloudpdfApiException(
+ message: 'API request failed',
+ statusCode: $statusCode,
+ body: $response->getBody()->getContents(),
+ );
+ }
+
+ /**
+ * Example:
+ * ```php
+ * $client->documents->thumbnail(
+ * 'tenantId',
+ * 'id',
+ * );
+ * ```
+ *
+ * @param string $tenantId
+ * @param string $id
+ * @param ?array{
+ * baseUrl?: string,
+ * maxRetries?: int,
+ * timeout?: float,
+ * headers?: array,
+ * queryParameters?: array,
+ * bodyProperties?: array,
+ * } $options
+ * @return string
+ * @throws CloudpdfException
+ * @throws CloudpdfApiException
+ */
+ public function thumbnail(string $tenantId, string $id, ?array $options = null): string
+ {
+ $options = array_merge($this->options, $options ?? []);
+ try {
+ $response = $this->client->sendRequest(
+ new JsonApiRequest(
+ baseUrl: $options['baseUrl'] ?? $this->client->options['baseUrl'] ?? '',
+ path: "v1/tenants/{$tenantId}/documents/{$id}/thumbnail",
+ method: HttpMethod::GET,
+ ),
+ $options,
+ );
+ $statusCode = $response->getStatusCode();
+ if ($statusCode >= 200 && $statusCode < 400) {
+ return $response->getBody()->getContents();
+ }
+ } catch (ClientExceptionInterface $e) {
+ throw new CloudpdfException(message: $e->getMessage(), previous: $e);
+ }
+ throw new CloudpdfApiException(
+ message: 'API request failed',
+ statusCode: $statusCode,
+ body: $response->getBody()->getContents(),
+ );
+ }
+
+ /**
+ * @param string $tenantId
+ * @param string $id
+ * @param ?array{
+ * baseUrl?: string,
+ * maxRetries?: int,
+ * timeout?: float,
+ * headers?: array,
+ * queryParameters?: array,
+ * bodyProperties?: array,
+ * } $options
+ * @return ?DocumentsUploadDirect200Response
+ * @throws CloudpdfException
+ * @throws CloudpdfApiException
+ */
+ public function uploadDirect(string $tenantId, string $id, ?array $options = null): ?DocumentsUploadDirect200Response
+ {
+ $options = array_merge($this->options, $options ?? []);
+ try {
+ $response = $this->client->sendRequest(
+ new JsonApiRequest(
+ baseUrl: $options['baseUrl'] ?? $this->client->options['baseUrl'] ?? '',
+ path: "v1/tenants/{$tenantId}/documents/{$id}/upload-direct",
+ method: HttpMethod::POST,
+ ),
+ $options,
+ );
+ $statusCode = $response->getStatusCode();
+ if ($statusCode >= 200 && $statusCode < 400) {
+ $json = $response->getBody()->getContents();
+ if (empty($json)) {
+ return null;
+ }
+ return DocumentsUploadDirect200Response::fromJson($json);
+ }
+ } catch (JsonException $e) {
+ throw new CloudpdfException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e);
+ } catch (ClientExceptionInterface $e) {
+ throw new CloudpdfException(message: $e->getMessage(), previous: $e);
+ }
+ throw new CloudpdfApiException(
+ message: 'API request failed',
+ statusCode: $statusCode,
+ body: $response->getBody()->getContents(),
+ );
+ }
+
+ /**
+ * Example:
+ * ```php
+ * $client->documents->init(
+ * 'tenantId',
+ * new DocumentsInitRequest([
+ * 'contentLength' => 1.1,
+ * 'contentSha256' => 'contentSha256',
+ * ]),
+ * );
+ * ```
+ *
+ * @param string $tenantId
+ * @param DocumentsInitRequest $request
+ * @param ?array{
+ * baseUrl?: string,
+ * maxRetries?: int,
+ * timeout?: float,
+ * headers?: array,
+ * queryParameters?: array,
+ * bodyProperties?: array,
+ * } $options
+ * @return ?DocumentsInit200Response
+ * @throws CloudpdfException
+ * @throws CloudpdfApiException
+ */
+ public function init(string $tenantId, DocumentsInitRequest $request, ?array $options = null): ?DocumentsInit200Response
+ {
+ $options = array_merge($this->options, $options ?? []);
+ try {
+ $response = $this->client->sendRequest(
+ new JsonApiRequest(
+ baseUrl: $options['baseUrl'] ?? $this->client->options['baseUrl'] ?? '',
+ path: "v1/tenants/{$tenantId}/documents/init",
+ method: HttpMethod::POST,
+ body: $request,
+ ),
+ $options,
+ );
+ $statusCode = $response->getStatusCode();
+ if ($statusCode >= 200 && $statusCode < 400) {
+ $json = $response->getBody()->getContents();
+ if (empty($json)) {
+ return null;
+ }
+ return DocumentsInit200Response::fromJson($json);
+ }
+ } catch (JsonException $e) {
+ throw new CloudpdfException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e);
+ } catch (ClientExceptionInterface $e) {
+ throw new CloudpdfException(message: $e->getMessage(), previous: $e);
+ }
+ throw new CloudpdfApiException(
+ message: 'API request failed',
+ statusCode: $statusCode,
+ body: $response->getBody()->getContents(),
+ );
+ }
+}
diff --git a/src/Documents/Requests/DocumentsCommitRequest.php b/src/Documents/Requests/DocumentsCommitRequest.php
new file mode 100644
index 0000000..0a4e074
--- /dev/null
+++ b/src/Documents/Requests/DocumentsCommitRequest.php
@@ -0,0 +1,26 @@
+sha256 = $values['sha256'];
+ }
+}
diff --git a/src/Documents/Requests/DocumentsInitRequest.php b/src/Documents/Requests/DocumentsInitRequest.php
new file mode 100644
index 0000000..72ea47e
--- /dev/null
+++ b/src/Documents/Requests/DocumentsInitRequest.php
@@ -0,0 +1,76 @@
+ $metadata
+ */
+ #[JsonProperty('metadata'), ArrayType(['string' => 'mixed'])]
+ public ?array $metadata;
+
+ /**
+ * @var ?string $idempotencyKey
+ */
+ #[JsonProperty('idempotencyKey')]
+ public ?string $idempotencyKey;
+
+ /**
+ * @var ?value-of $dedupMode
+ */
+ #[JsonProperty('dedupMode')]
+ public ?string $dedupMode;
+
+ /**
+ * @var ?string $docId
+ */
+ #[JsonProperty('docId')]
+ public ?string $docId;
+
+ /**
+ * @var ?float $uploadTtlSec
+ */
+ #[JsonProperty('uploadTtlSec')]
+ public ?float $uploadTtlSec;
+
+ /**
+ * @param array{
+ * contentLength: float,
+ * contentSha256: string,
+ * metadata?: ?array,
+ * idempotencyKey?: ?string,
+ * dedupMode?: ?value-of,
+ * docId?: ?string,
+ * uploadTtlSec?: ?float,
+ * } $values
+ */
+ public function __construct(
+ array $values,
+ ) {
+ $this->contentLength = $values['contentLength'];
+ $this->contentSha256 = $values['contentSha256'];
+ $this->metadata = $values['metadata'] ?? null;
+ $this->idempotencyKey = $values['idempotencyKey'] ?? null;
+ $this->dedupMode = $values['dedupMode'] ?? null;
+ $this->docId = $values['docId'] ?? null;
+ $this->uploadTtlSec = $values['uploadTtlSec'] ?? null;
+ }
+}
diff --git a/src/Documents/Requests/ListDocumentsRequest.php b/src/Documents/Requests/ListDocumentsRequest.php
new file mode 100644
index 0000000..3e577b0
--- /dev/null
+++ b/src/Documents/Requests/ListDocumentsRequest.php
@@ -0,0 +1,39 @@
+ $state
+ */
+ public ?string $state;
+
+ /**
+ * @param array{
+ * limit?: ?int,
+ * cursor?: ?string,
+ * state?: ?value-of,
+ * } $values
+ */
+ public function __construct(
+ array $values = [],
+ ) {
+ $this->limit = $values['limit'] ?? null;
+ $this->cursor = $values['cursor'] ?? null;
+ $this->state = $values['state'] ?? null;
+ }
+}
diff --git a/src/Documents/Types/DocumentsInitRequestDedupMode.php b/src/Documents/Types/DocumentsInitRequestDedupMode.php
new file mode 100644
index 0000000..7165f98
--- /dev/null
+++ b/src/Documents/Types/DocumentsInitRequestDedupMode.php
@@ -0,0 +1,9 @@
+body = $body;
+ parent::__construct($message, $statusCode, $previous);
+ }
+
+ /**
+ * Returns the body of the response that triggered the exception.
+ *
+ * @return mixed
+ */
+ public function getBody(): mixed
+ {
+ return $this->body;
+ }
+
+ /**
+ * @return string
+ */
+ public function __toString(): string
+ {
+ if (empty($this->body)) {
+ return $this->message . '; Status Code: ' . $this->getCode() . "\n";
+ }
+ return $this->message . '; Status Code: ' . $this->getCode() . '; Body: ' . print_r($this->body, true) . "\n";
+ }
+}
diff --git a/src/Exceptions/CloudpdfException.php b/src/Exceptions/CloudpdfException.php
new file mode 100644
index 0000000..6fed674
--- /dev/null
+++ b/src/Exceptions/CloudpdfException.php
@@ -0,0 +1,12 @@
+limit = $values['limit'] ?? null;
+ $this->cursor = $values['cursor'] ?? null;
+ }
+}
diff --git a/src/Tenants/Requests/TenantsCreateRequest.php b/src/Tenants/Requests/TenantsCreateRequest.php
new file mode 100644
index 0000000..39ae447
--- /dev/null
+++ b/src/Tenants/Requests/TenantsCreateRequest.php
@@ -0,0 +1,34 @@
+id = $values['id'];
+ $this->name = $values['name'] ?? null;
+ }
+}
diff --git a/src/Tenants/TenantsClient.php b/src/Tenants/TenantsClient.php
new file mode 100644
index 0000000..a35a0f7
--- /dev/null
+++ b/src/Tenants/TenantsClient.php
@@ -0,0 +1,272 @@
+,
+ * } $options @phpstan-ignore-next-line Property is used in endpoint methods via HttpEndpointGenerator
+ */
+ private array $options;
+
+ /**
+ * @var RawClient $client
+ */
+ private RawClient $client;
+
+ /**
+ * @param RawClient $client
+ * @param ?array{
+ * baseUrl?: string,
+ * client?: ClientInterface,
+ * maxRetries?: int,
+ * timeout?: float,
+ * headers?: array,
+ * } $options
+ */
+ public function __construct(
+ RawClient $client,
+ ?array $options = null,
+ ) {
+ $this->client = $client;
+ $this->options = $options ?? [];
+ }
+
+ /**
+ * Example:
+ * ```php
+ * $client->tenants->list(
+ * new ListTenantsRequest([]),
+ * );
+ * ```
+ *
+ * @param ListTenantsRequest $request
+ * @param ?array{
+ * baseUrl?: string,
+ * maxRetries?: int,
+ * timeout?: float,
+ * headers?: array,
+ * queryParameters?: array,
+ * bodyProperties?: array,
+ * } $options
+ * @return ?TenantsList200Response
+ * @throws CloudpdfException
+ * @throws CloudpdfApiException
+ */
+ public function list(ListTenantsRequest $request = new ListTenantsRequest(), ?array $options = null): ?TenantsList200Response
+ {
+ $options = array_merge($this->options, $options ?? []);
+ $query = [];
+ if ($request->limit != null) {
+ $query['limit'] = $request->limit;
+ }
+ if ($request->cursor != null) {
+ $query['cursor'] = $request->cursor;
+ }
+ try {
+ $response = $this->client->sendRequest(
+ new JsonApiRequest(
+ baseUrl: $options['baseUrl'] ?? $this->client->options['baseUrl'] ?? '',
+ path: "v1/tenants",
+ method: HttpMethod::GET,
+ query: $query,
+ ),
+ $options,
+ );
+ $statusCode = $response->getStatusCode();
+ if ($statusCode >= 200 && $statusCode < 400) {
+ $json = $response->getBody()->getContents();
+ if (empty($json)) {
+ return null;
+ }
+ return TenantsList200Response::fromJson($json);
+ }
+ } catch (JsonException $e) {
+ throw new CloudpdfException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e);
+ } catch (ClientExceptionInterface $e) {
+ throw new CloudpdfException(message: $e->getMessage(), previous: $e);
+ }
+ throw new CloudpdfApiException(
+ message: 'API request failed',
+ statusCode: $statusCode,
+ body: $response->getBody()->getContents(),
+ );
+ }
+
+ /**
+ * Example:
+ * ```php
+ * $client->tenants->create(
+ * new TenantsCreateRequest([
+ * 'id' => 'id',
+ * ]),
+ * );
+ * ```
+ *
+ * @param TenantsCreateRequest $request
+ * @param ?array{
+ * baseUrl?: string,
+ * maxRetries?: int,
+ * timeout?: float,
+ * headers?: array,
+ * queryParameters?: array,
+ * bodyProperties?: array,
+ * } $options
+ * @return ?TenantsCreate200Response
+ * @throws CloudpdfException
+ * @throws CloudpdfApiException
+ */
+ public function create(TenantsCreateRequest $request, ?array $options = null): ?TenantsCreate200Response
+ {
+ $options = array_merge($this->options, $options ?? []);
+ try {
+ $response = $this->client->sendRequest(
+ new JsonApiRequest(
+ baseUrl: $options['baseUrl'] ?? $this->client->options['baseUrl'] ?? '',
+ path: "v1/tenants",
+ method: HttpMethod::POST,
+ body: $request,
+ ),
+ $options,
+ );
+ $statusCode = $response->getStatusCode();
+ if ($statusCode >= 200 && $statusCode < 400) {
+ $json = $response->getBody()->getContents();
+ if (empty($json)) {
+ return null;
+ }
+ return TenantsCreate200Response::fromJson($json);
+ }
+ } catch (JsonException $e) {
+ throw new CloudpdfException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e);
+ } catch (ClientExceptionInterface $e) {
+ throw new CloudpdfException(message: $e->getMessage(), previous: $e);
+ }
+ throw new CloudpdfApiException(
+ message: 'API request failed',
+ statusCode: $statusCode,
+ body: $response->getBody()->getContents(),
+ );
+ }
+
+ /**
+ * Example:
+ * ```php
+ * $client->tenants->get(
+ * 'tenantId',
+ * );
+ * ```
+ *
+ * @param string $tenantId
+ * @param ?array{
+ * baseUrl?: string,
+ * maxRetries?: int,
+ * timeout?: float,
+ * headers?: array,
+ * queryParameters?: array,
+ * bodyProperties?: array,
+ * } $options
+ * @return ?TenantsGet200Response
+ * @throws CloudpdfException
+ * @throws CloudpdfApiException
+ */
+ public function get(string $tenantId, ?array $options = null): ?TenantsGet200Response
+ {
+ $options = array_merge($this->options, $options ?? []);
+ try {
+ $response = $this->client->sendRequest(
+ new JsonApiRequest(
+ baseUrl: $options['baseUrl'] ?? $this->client->options['baseUrl'] ?? '',
+ path: "v1/tenants/{$tenantId}",
+ method: HttpMethod::GET,
+ ),
+ $options,
+ );
+ $statusCode = $response->getStatusCode();
+ if ($statusCode >= 200 && $statusCode < 400) {
+ $json = $response->getBody()->getContents();
+ if (empty($json)) {
+ return null;
+ }
+ return TenantsGet200Response::fromJson($json);
+ }
+ } catch (JsonException $e) {
+ throw new CloudpdfException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e);
+ } catch (ClientExceptionInterface $e) {
+ throw new CloudpdfException(message: $e->getMessage(), previous: $e);
+ }
+ throw new CloudpdfApiException(
+ message: 'API request failed',
+ statusCode: $statusCode,
+ body: $response->getBody()->getContents(),
+ );
+ }
+
+ /**
+ * Destroys the tenant and everything in its namespace — documents, layers, stored bytes, audit history. Irreversible.
+ *
+ * Example:
+ * ```php
+ * $client->tenants->delete(
+ * 'tenantId',
+ * );
+ * ```
+ *
+ * @param string $tenantId
+ * @param ?array{
+ * baseUrl?: string,
+ * maxRetries?: int,
+ * timeout?: float,
+ * headers?: array,
+ * queryParameters?: array,
+ * bodyProperties?: array,
+ * } $options
+ * @throws CloudpdfException
+ * @throws CloudpdfApiException
+ */
+ public function delete(string $tenantId, ?array $options = null): void
+ {
+ $options = array_merge($this->options, $options ?? []);
+ try {
+ $response = $this->client->sendRequest(
+ new JsonApiRequest(
+ baseUrl: $options['baseUrl'] ?? $this->client->options['baseUrl'] ?? '',
+ path: "v1/tenants/{$tenantId}",
+ method: HttpMethod::DELETE,
+ ),
+ $options,
+ );
+ $statusCode = $response->getStatusCode();
+ if ($statusCode >= 200 && $statusCode < 400) {
+ return;
+ }
+ } catch (ClientExceptionInterface $e) {
+ throw new CloudpdfException(message: $e->getMessage(), previous: $e);
+ }
+ throw new CloudpdfApiException(
+ message: 'API request failed',
+ statusCode: $statusCode,
+ body: $response->getBody()->getContents(),
+ );
+ }
+}
diff --git a/src/Tokens/Requests/IssueTokensRequest.php b/src/Tokens/Requests/IssueTokensRequest.php
new file mode 100644
index 0000000..4a433ea
--- /dev/null
+++ b/src/Tokens/Requests/IssueTokensRequest.php
@@ -0,0 +1,25 @@
+body = $values['body'];
+ }
+}
diff --git a/src/Tokens/Requests/TokensRevokeRequest.php b/src/Tokens/Requests/TokensRevokeRequest.php
new file mode 100644
index 0000000..9fc25ce
--- /dev/null
+++ b/src/Tokens/Requests/TokensRevokeRequest.php
@@ -0,0 +1,34 @@
+reason = $values['reason'] ?? null;
+ $this->expiresAtSeconds = $values['expiresAtSeconds'] ?? null;
+ }
+}
diff --git a/src/Tokens/TokensClient.php b/src/Tokens/TokensClient.php
new file mode 100644
index 0000000..18221fd
--- /dev/null
+++ b/src/Tokens/TokensClient.php
@@ -0,0 +1,172 @@
+,
+ * } $options @phpstan-ignore-next-line Property is used in endpoint methods via HttpEndpointGenerator
+ */
+ private array $options;
+
+ /**
+ * @var RawClient $client
+ */
+ private RawClient $client;
+
+ /**
+ * @param RawClient $client
+ * @param ?array{
+ * baseUrl?: string,
+ * client?: ClientInterface,
+ * maxRetries?: int,
+ * timeout?: float,
+ * headers?: array,
+ * } $options
+ */
+ public function __construct(
+ RawClient $client,
+ ?array $options = null,
+ ) {
+ $this->client = $client;
+ $this->options = $options ?? [];
+ }
+
+ /**
+ * kind "tenant" requires the API token — authority mints only downward. Mounted only when the deployment can sign (HS256 mode); asymmetric deployments mint with their own private key.
+ *
+ * Example:
+ * ```php
+ * $client->tokens->issue(
+ * 'tenantId',
+ * new IssueTokensRequest([
+ * 'body' => TokensIssueRequest::doc(new TokensIssueRequestDoc([
+ * 'sub' => 'sub',
+ * 'docId' => 'docId',
+ * 'scope' => [
+ * 'scope',
+ * ],
+ * 'expiresIn' => 1,
+ * ])),
+ * ]),
+ * );
+ * ```
+ *
+ * @param string $tenantId
+ * @param IssueTokensRequest $request
+ * @param ?array{
+ * baseUrl?: string,
+ * maxRetries?: int,
+ * timeout?: float,
+ * headers?: array,
+ * queryParameters?: array,
+ * bodyProperties?: array,
+ * } $options
+ * @return ?TokensIssue200Response
+ * @throws CloudpdfException
+ * @throws CloudpdfApiException
+ */
+ public function issue(string $tenantId, IssueTokensRequest $request, ?array $options = null): ?TokensIssue200Response
+ {
+ $options = array_merge($this->options, $options ?? []);
+ try {
+ $response = $this->client->sendRequest(
+ new JsonApiRequest(
+ baseUrl: $options['baseUrl'] ?? $this->client->options['baseUrl'] ?? '',
+ path: "v1/tenants/{$tenantId}/tokens",
+ method: HttpMethod::POST,
+ body: $request->body,
+ ),
+ $options,
+ );
+ $statusCode = $response->getStatusCode();
+ if ($statusCode >= 200 && $statusCode < 400) {
+ $json = $response->getBody()->getContents();
+ if (empty($json)) {
+ return null;
+ }
+ return TokensIssue200Response::fromJson($json);
+ }
+ } catch (JsonException $e) {
+ throw new CloudpdfException(message: "Failed to deserialize response: {$e->getMessage()}", previous: $e);
+ } catch (ClientExceptionInterface $e) {
+ throw new CloudpdfException(message: $e->getMessage(), previous: $e);
+ }
+ throw new CloudpdfApiException(
+ message: 'API request failed',
+ statusCode: $statusCode,
+ body: $response->getBody()->getContents(),
+ );
+ }
+
+ /**
+ * Mounted only when the deployment enables token revocation.
+ *
+ * Example:
+ * ```php
+ * $client->tokens->revoke(
+ * 'tenantId',
+ * 'jti',
+ * new TokensRevokeRequest([]),
+ * );
+ * ```
+ *
+ * @param string $tenantId
+ * @param string $jti
+ * @param TokensRevokeRequest $request
+ * @param ?array{
+ * baseUrl?: string,
+ * maxRetries?: int,
+ * timeout?: float,
+ * headers?: array,
+ * queryParameters?: array,
+ * bodyProperties?: array,
+ * } $options
+ * @throws CloudpdfException
+ * @throws CloudpdfApiException
+ */
+ public function revoke(string $tenantId, string $jti, TokensRevokeRequest $request = new TokensRevokeRequest(), ?array $options = null): void
+ {
+ $options = array_merge($this->options, $options ?? []);
+ try {
+ $response = $this->client->sendRequest(
+ new JsonApiRequest(
+ baseUrl: $options['baseUrl'] ?? $this->client->options['baseUrl'] ?? '',
+ path: "v1/tenants/{$tenantId}/tokens/{$jti}/revoke",
+ method: HttpMethod::POST,
+ body: $request,
+ ),
+ $options,
+ );
+ $statusCode = $response->getStatusCode();
+ if ($statusCode >= 200 && $statusCode < 400) {
+ return;
+ }
+ } catch (ClientExceptionInterface $e) {
+ throw new CloudpdfException(message: $e->getMessage(), previous: $e);
+ }
+ throw new CloudpdfApiException(
+ message: 'API request failed',
+ statusCode: $statusCode,
+ body: $response->getBody()->getContents(),
+ );
+ }
+}
diff --git a/src/Types/AdminErrorPayload.php b/src/Types/AdminErrorPayload.php
new file mode 100644
index 0000000..b0d7b00
--- /dev/null
+++ b/src/Types/AdminErrorPayload.php
@@ -0,0 +1,34 @@
+error = $values['error'];
+ }
+
+ /**
+ * @return string
+ */
+ public function __toString(): string
+ {
+ return $this->toJson();
+ }
+}
diff --git a/src/Types/AdminErrorPayloadError.php b/src/Types/AdminErrorPayloadError.php
new file mode 100644
index 0000000..481eca6
--- /dev/null
+++ b/src/Types/AdminErrorPayloadError.php
@@ -0,0 +1,42 @@
+code = $values['code'];
+ $this->message = $values['message'];
+ }
+
+ /**
+ * @return string
+ */
+ public function __toString(): string
+ {
+ return $this->toJson();
+ }
+}
diff --git a/src/Types/DeploymentLicenseStatusResponse.php b/src/Types/DeploymentLicenseStatusResponse.php
new file mode 100644
index 0000000..965ea89
--- /dev/null
+++ b/src/Types/DeploymentLicenseStatusResponse.php
@@ -0,0 +1,50 @@
+license = $values['license'] ?? null;
+ $this->reporting = $values['reporting'] ?? null;
+ $this->usage = $values['usage'] ?? null;
+ }
+
+ /**
+ * @return string
+ */
+ public function __toString(): string
+ {
+ return $this->toJson();
+ }
+}
diff --git a/src/Types/DocAnnotationsCreate200Response.php b/src/Types/DocAnnotationsCreate200Response.php
new file mode 100644
index 0000000..04f076a
--- /dev/null
+++ b/src/Types/DocAnnotationsCreate200Response.php
@@ -0,0 +1,34 @@
+meta = $values['meta'];
+ }
+
+ /**
+ * @return string
+ */
+ public function __toString(): string
+ {
+ return $this->toJson();
+ }
+}
diff --git a/src/Types/DocAnnotationsCreate200ResponseMeta.php b/src/Types/DocAnnotationsCreate200ResponseMeta.php
new file mode 100644
index 0000000..7c9d7d3
--- /dev/null
+++ b/src/Types/DocAnnotationsCreate200ResponseMeta.php
@@ -0,0 +1,43 @@
+ $affectedPages
+ */
+ #[JsonProperty('affectedPages'), ArrayType([DocAnnotationsCreate200ResponseMetaAffectedPagesItem::class])]
+ public array $affectedPages;
+
+ /**
+ * @var ?DocAnnotationsCreate200ResponseMetaCacheDelta $cacheDelta
+ */
+ #[JsonProperty('cacheDelta')]
+ public ?DocAnnotationsCreate200ResponseMetaCacheDelta $cacheDelta;
+
+ /**
+ * @param array{
+ * affectedPages: array,
+ * cacheDelta?: ?DocAnnotationsCreate200ResponseMetaCacheDelta,
+ * } $values
+ */
+ public function __construct(
+ array $values,
+ ) {
+ $this->affectedPages = $values['affectedPages'];
+ $this->cacheDelta = $values['cacheDelta'] ?? null;
+ }
+
+ /**
+ * @return string
+ */
+ public function __toString(): string
+ {
+ return $this->toJson();
+ }
+}
diff --git a/src/Types/DocAnnotationsCreate200ResponseMetaAffectedPagesItem.php b/src/Types/DocAnnotationsCreate200ResponseMetaAffectedPagesItem.php
new file mode 100644
index 0000000..78beed3
--- /dev/null
+++ b/src/Types/DocAnnotationsCreate200ResponseMetaAffectedPagesItem.php
@@ -0,0 +1,50 @@
+pageObjectNumber = $values['pageObjectNumber'];
+ $this->revision = $values['revision'];
+ $this->weakAnnotationState = $values['weakAnnotationState'];
+ }
+
+ /**
+ * @return string
+ */
+ public function __toString(): string
+ {
+ return $this->toJson();
+ }
+}
diff --git a/src/Types/DocAnnotationsCreate200ResponseMetaAffectedPagesItemRevision.php b/src/Types/DocAnnotationsCreate200ResponseMetaAffectedPagesItemRevision.php
new file mode 100644
index 0000000..d6dc426
--- /dev/null
+++ b/src/Types/DocAnnotationsCreate200ResponseMetaAffectedPagesItemRevision.php
@@ -0,0 +1,50 @@
+docSessionId = $values['docSessionId'];
+ $this->pageObjectNumber = $values['pageObjectNumber'];
+ $this->generation = $values['generation'];
+ }
+
+ /**
+ * @return string
+ */
+ public function __toString(): string
+ {
+ return $this->toJson();
+ }
+}
diff --git a/src/Types/DocAnnotationsCreate200ResponseMetaAffectedPagesItemWeakAnnotationState.php b/src/Types/DocAnnotationsCreate200ResponseMetaAffectedPagesItemWeakAnnotationState.php
new file mode 100644
index 0000000..f3ef3cd
--- /dev/null
+++ b/src/Types/DocAnnotationsCreate200ResponseMetaAffectedPagesItemWeakAnnotationState.php
@@ -0,0 +1,196 @@
+kind = $values['kind'];
+ $this->value = $values['value'];
+ }
+
+ /**
+ * @param DocAnnotationsCreate200ResponseMetaAffectedPagesItemWeakAnnotationStateUnknown $unknown
+ * @return DocAnnotationsCreate200ResponseMetaAffectedPagesItemWeakAnnotationState
+ */
+ public static function unknown(DocAnnotationsCreate200ResponseMetaAffectedPagesItemWeakAnnotationStateUnknown $unknown): DocAnnotationsCreate200ResponseMetaAffectedPagesItemWeakAnnotationState
+ {
+ return new DocAnnotationsCreate200ResponseMetaAffectedPagesItemWeakAnnotationState([
+ 'kind' => 'unknown',
+ 'value' => $unknown,
+ ]);
+ }
+
+ /**
+ * @param DocAnnotationsCreate200ResponseMetaAffectedPagesItemWeakAnnotationStateKnown $known
+ * @return DocAnnotationsCreate200ResponseMetaAffectedPagesItemWeakAnnotationState
+ */
+ public static function known(DocAnnotationsCreate200ResponseMetaAffectedPagesItemWeakAnnotationStateKnown $known): DocAnnotationsCreate200ResponseMetaAffectedPagesItemWeakAnnotationState
+ {
+ return new DocAnnotationsCreate200ResponseMetaAffectedPagesItemWeakAnnotationState([
+ 'kind' => 'known',
+ 'value' => $known,
+ ]);
+ }
+
+ /**
+ * @return bool
+ */
+ public function isUnknown(): bool
+ {
+ return $this->value instanceof DocAnnotationsCreate200ResponseMetaAffectedPagesItemWeakAnnotationStateUnknown && $this->kind === 'unknown';
+ }
+
+ /**
+ * @return DocAnnotationsCreate200ResponseMetaAffectedPagesItemWeakAnnotationStateUnknown
+ */
+ public function asUnknown(): DocAnnotationsCreate200ResponseMetaAffectedPagesItemWeakAnnotationStateUnknown
+ {
+ if (!($this->value instanceof DocAnnotationsCreate200ResponseMetaAffectedPagesItemWeakAnnotationStateUnknown && $this->kind === 'unknown')) {
+ throw new Exception(
+ "Expected unknown; got " . $this->kind . " with value of type " . get_debug_type($this->value),
+ );
+ }
+
+ return $this->value;
+ }
+
+ /**
+ * @return bool
+ */
+ public function isKnown(): bool
+ {
+ return $this->value instanceof DocAnnotationsCreate200ResponseMetaAffectedPagesItemWeakAnnotationStateKnown && $this->kind === 'known';
+ }
+
+ /**
+ * @return DocAnnotationsCreate200ResponseMetaAffectedPagesItemWeakAnnotationStateKnown
+ */
+ public function asKnown(): DocAnnotationsCreate200ResponseMetaAffectedPagesItemWeakAnnotationStateKnown
+ {
+ if (!($this->value instanceof DocAnnotationsCreate200ResponseMetaAffectedPagesItemWeakAnnotationStateKnown && $this->kind === 'known')) {
+ throw new Exception(
+ "Expected known; got " . $this->kind . " with value of type " . get_debug_type($this->value),
+ );
+ }
+
+ return $this->value;
+ }
+
+ /**
+ * @return string
+ */
+ public function __toString(): string
+ {
+ return $this->toJson();
+ }
+
+ /**
+ * @return array
+ */
+ public function jsonSerialize(): array
+ {
+ $result = [];
+ $result['kind'] = $this->kind;
+
+ $base = parent::jsonSerialize();
+ $result = array_merge($base, $result);
+
+ switch ($this->kind) {
+ case 'unknown':
+ $value = $this->asUnknown()->jsonSerialize();
+ $result = array_merge($value, $result);
+ break;
+ case 'known':
+ $value = $this->asKnown()->jsonSerialize();
+ $result = array_merge($value, $result);
+ break;
+ case '_unknown':
+ default:
+ if (is_null($this->value)) {
+ break;
+ }
+ if ($this->value instanceof JsonSerializableType) {
+ $value = $this->value->jsonSerialize();
+ $result = array_merge($value, $result);
+ } elseif (is_array($this->value)) {
+ $result = array_merge($this->value, $result);
+ }
+ }
+
+ return $result;
+ }
+
+ /**
+ * @param array $data
+ */
+ public static function jsonDeserialize(array $data): static
+ {
+ $args = [];
+ if (!array_key_exists('kind', $data)) {
+ throw new Exception(
+ "JSON data is missing property 'kind'",
+ );
+ }
+ $kind = $data['kind'];
+ if (!(is_string($kind))) {
+ throw new Exception(
+ "Expected property 'kind' in JSON data to be string, instead received " . get_debug_type($data['kind']),
+ );
+ }
+
+ $args['kind'] = $kind;
+ switch ($kind) {
+ case 'unknown':
+ $args['value'] = DocAnnotationsCreate200ResponseMetaAffectedPagesItemWeakAnnotationStateUnknown::jsonDeserialize($data);
+ break;
+ case 'known':
+ $args['value'] = DocAnnotationsCreate200ResponseMetaAffectedPagesItemWeakAnnotationStateKnown::jsonDeserialize($data);
+ break;
+ case '_unknown':
+ default:
+ $args['kind'] = '_unknown';
+ $args['value'] = $data;
+ }
+
+ // @phpstan-ignore-next-line
+ return new static($args);
+ }
+}
diff --git a/src/Types/DocAnnotationsCreate200ResponseMetaAffectedPagesItemWeakAnnotationStateKnown.php b/src/Types/DocAnnotationsCreate200ResponseMetaAffectedPagesItemWeakAnnotationStateKnown.php
new file mode 100644
index 0000000..f88ba9e
--- /dev/null
+++ b/src/Types/DocAnnotationsCreate200ResponseMetaAffectedPagesItemWeakAnnotationStateKnown.php
@@ -0,0 +1,34 @@
+hasAnyWeakAnnotations = $values['hasAnyWeakAnnotations'];
+ }
+
+ /**
+ * @return string
+ */
+ public function __toString(): string
+ {
+ return $this->toJson();
+ }
+}
diff --git a/src/Types/DocAnnotationsCreate200ResponseMetaAffectedPagesItemWeakAnnotationStateUnknown.php b/src/Types/DocAnnotationsCreate200ResponseMetaAffectedPagesItemWeakAnnotationStateUnknown.php
new file mode 100644
index 0000000..b787b4a
--- /dev/null
+++ b/src/Types/DocAnnotationsCreate200ResponseMetaAffectedPagesItemWeakAnnotationStateUnknown.php
@@ -0,0 +1,26 @@
+toJson();
+ }
+}
diff --git a/src/Types/DocAnnotationsCreate200ResponseMetaCacheDelta.php b/src/Types/DocAnnotationsCreate200ResponseMetaCacheDelta.php
new file mode 100644
index 0000000..971a44b
--- /dev/null
+++ b/src/Types/DocAnnotationsCreate200ResponseMetaCacheDelta.php
@@ -0,0 +1,51 @@
+ $pages
+ */
+ #[JsonProperty('pages'), ArrayType([DocAnnotationsCreate200ResponseMetaCacheDeltaPagesItem::class])]
+ public array $pages;
+
+ /**
+ * @param array{
+ * previousDocVersion: int,
+ * docVersion: int,
+ * pages: array,
+ * } $values
+ */
+ public function __construct(
+ array $values,
+ ) {
+ $this->previousDocVersion = $values['previousDocVersion'];
+ $this->docVersion = $values['docVersion'];
+ $this->pages = $values['pages'];
+ }
+
+ /**
+ * @return string
+ */
+ public function __toString(): string
+ {
+ return $this->toJson();
+ }
+}
diff --git a/src/Types/DocAnnotationsCreate200ResponseMetaCacheDeltaPagesItem.php b/src/Types/DocAnnotationsCreate200ResponseMetaCacheDeltaPagesItem.php
new file mode 100644
index 0000000..3702487
--- /dev/null
+++ b/src/Types/DocAnnotationsCreate200ResponseMetaCacheDeltaPagesItem.php
@@ -0,0 +1,42 @@
+pageObjectNumber = $values['pageObjectNumber'];
+ $this->cache = $values['cache'];
+ }
+
+ /**
+ * @return string
+ */
+ public function __toString(): string
+ {
+ return $this->toJson();
+ }
+}
diff --git a/src/Types/DocAnnotationsCreate200ResponseMetaCacheDeltaPagesItemCache.php b/src/Types/DocAnnotationsCreate200ResponseMetaCacheDeltaPagesItemCache.php
new file mode 100644
index 0000000..e80683d
--- /dev/null
+++ b/src/Types/DocAnnotationsCreate200ResponseMetaCacheDeltaPagesItemCache.php
@@ -0,0 +1,42 @@
+contentVersion = $values['contentVersion'];
+ $this->annotationVersion = $values['annotationVersion'];
+ }
+
+ /**
+ * @return string
+ */
+ public function __toString(): string
+ {
+ return $this->toJson();
+ }
+}
diff --git a/src/Types/DocAnnotationsCreate400Response.php b/src/Types/DocAnnotationsCreate400Response.php
new file mode 100644
index 0000000..214e04a
--- /dev/null
+++ b/src/Types/DocAnnotationsCreate400Response.php
@@ -0,0 +1,59 @@
+ $name
+ */
+ #[JsonProperty('name')]
+ public string $name;
+
+ /**
+ * @var value-of $code
+ */
+ #[JsonProperty('code')]
+ public string $code;
+
+ /**
+ * @var string $message
+ */
+ #[JsonProperty('message')]
+ public string $message;
+
+ /**
+ * @var ?array $details
+ */
+ #[JsonProperty('details'), ArrayType(['string' => 'mixed'])]
+ public ?array $details;
+
+ /**
+ * @param array{
+ * name: value-of,
+ * code: value-of,
+ * message: string,
+ * details?: ?array,
+ * } $values
+ */
+ public function __construct(
+ array $values,
+ ) {
+ $this->name = $values['name'];
+ $this->code = $values['code'];
+ $this->message = $values['message'];
+ $this->details = $values['details'] ?? null;
+ }
+
+ /**
+ * @return string
+ */
+ public function __toString(): string
+ {
+ return $this->toJson();
+ }
+}
diff --git a/src/Types/DocAnnotationsCreate400ResponseCode.php b/src/Types/DocAnnotationsCreate400ResponseCode.php
new file mode 100644
index 0000000..381b015
--- /dev/null
+++ b/src/Types/DocAnnotationsCreate400ResponseCode.php
@@ -0,0 +1,25 @@
+ $name
+ */
+ #[JsonProperty('name')]
+ public string $name;
+
+ /**
+ * @var value-of $code
+ */
+ #[JsonProperty('code')]
+ public string $code;
+
+ /**
+ * @var string $message
+ */
+ #[JsonProperty('message')]
+ public string $message;
+
+ /**
+ * @var ?array $details
+ */
+ #[JsonProperty('details'), ArrayType(['string' => 'mixed'])]
+ public ?array $details;
+
+ /**
+ * @param array{
+ * name: value-of,
+ * code: value-of,
+ * message: string,
+ * details?: ?array,
+ * } $values
+ */
+ public function __construct(
+ array $values,
+ ) {
+ $this->name = $values['name'];
+ $this->code = $values['code'];
+ $this->message = $values['message'];
+ $this->details = $values['details'] ?? null;
+ }
+
+ /**
+ * @return string
+ */
+ public function __toString(): string
+ {
+ return $this->toJson();
+ }
+}
diff --git a/src/Types/DocAnnotationsCreate404ResponseCode.php b/src/Types/DocAnnotationsCreate404ResponseCode.php
new file mode 100644
index 0000000..c63bc3a
--- /dev/null
+++ b/src/Types/DocAnnotationsCreate404ResponseCode.php
@@ -0,0 +1,25 @@
+meta = $values['meta'];
+ }
+
+ /**
+ * @return string
+ */
+ public function __toString(): string
+ {
+ return $this->toJson();
+ }
+}
diff --git a/src/Types/DocAnnotationsDelete200ResponseMeta.php b/src/Types/DocAnnotationsDelete200ResponseMeta.php
new file mode 100644
index 0000000..3e1088c
--- /dev/null
+++ b/src/Types/DocAnnotationsDelete200ResponseMeta.php
@@ -0,0 +1,43 @@
+ $affectedPages
+ */
+ #[JsonProperty('affectedPages'), ArrayType([DocAnnotationsDelete200ResponseMetaAffectedPagesItem::class])]
+ public array $affectedPages;
+
+ /**
+ * @var ?DocAnnotationsDelete200ResponseMetaCacheDelta $cacheDelta
+ */
+ #[JsonProperty('cacheDelta')]
+ public ?DocAnnotationsDelete200ResponseMetaCacheDelta $cacheDelta;
+
+ /**
+ * @param array{
+ * affectedPages: array,
+ * cacheDelta?: ?DocAnnotationsDelete200ResponseMetaCacheDelta,
+ * } $values
+ */
+ public function __construct(
+ array $values,
+ ) {
+ $this->affectedPages = $values['affectedPages'];
+ $this->cacheDelta = $values['cacheDelta'] ?? null;
+ }
+
+ /**
+ * @return string
+ */
+ public function __toString(): string
+ {
+ return $this->toJson();
+ }
+}
diff --git a/src/Types/DocAnnotationsDelete200ResponseMetaAffectedPagesItem.php b/src/Types/DocAnnotationsDelete200ResponseMetaAffectedPagesItem.php
new file mode 100644
index 0000000..6ab7d94
--- /dev/null
+++ b/src/Types/DocAnnotationsDelete200ResponseMetaAffectedPagesItem.php
@@ -0,0 +1,50 @@
+pageObjectNumber = $values['pageObjectNumber'];
+ $this->revision = $values['revision'];
+ $this->weakAnnotationState = $values['weakAnnotationState'];
+ }
+
+ /**
+ * @return string
+ */
+ public function __toString(): string
+ {
+ return $this->toJson();
+ }
+}
diff --git a/src/Types/DocAnnotationsDelete200ResponseMetaAffectedPagesItemRevision.php b/src/Types/DocAnnotationsDelete200ResponseMetaAffectedPagesItemRevision.php
new file mode 100644
index 0000000..125ff75
--- /dev/null
+++ b/src/Types/DocAnnotationsDelete200ResponseMetaAffectedPagesItemRevision.php
@@ -0,0 +1,50 @@
+docSessionId = $values['docSessionId'];
+ $this->pageObjectNumber = $values['pageObjectNumber'];
+ $this->generation = $values['generation'];
+ }
+
+ /**
+ * @return string
+ */
+ public function __toString(): string
+ {
+ return $this->toJson();
+ }
+}
diff --git a/src/Types/DocAnnotationsDelete200ResponseMetaAffectedPagesItemWeakAnnotationState.php b/src/Types/DocAnnotationsDelete200ResponseMetaAffectedPagesItemWeakAnnotationState.php
new file mode 100644
index 0000000..1fb07ae
--- /dev/null
+++ b/src/Types/DocAnnotationsDelete200ResponseMetaAffectedPagesItemWeakAnnotationState.php
@@ -0,0 +1,196 @@
+kind = $values['kind'];
+ $this->value = $values['value'];
+ }
+
+ /**
+ * @param DocAnnotationsDelete200ResponseMetaAffectedPagesItemWeakAnnotationStateUnknown $unknown
+ * @return DocAnnotationsDelete200ResponseMetaAffectedPagesItemWeakAnnotationState
+ */
+ public static function unknown(DocAnnotationsDelete200ResponseMetaAffectedPagesItemWeakAnnotationStateUnknown $unknown): DocAnnotationsDelete200ResponseMetaAffectedPagesItemWeakAnnotationState
+ {
+ return new DocAnnotationsDelete200ResponseMetaAffectedPagesItemWeakAnnotationState([
+ 'kind' => 'unknown',
+ 'value' => $unknown,
+ ]);
+ }
+
+ /**
+ * @param DocAnnotationsDelete200ResponseMetaAffectedPagesItemWeakAnnotationStateKnown $known
+ * @return DocAnnotationsDelete200ResponseMetaAffectedPagesItemWeakAnnotationState
+ */
+ public static function known(DocAnnotationsDelete200ResponseMetaAffectedPagesItemWeakAnnotationStateKnown $known): DocAnnotationsDelete200ResponseMetaAffectedPagesItemWeakAnnotationState
+ {
+ return new DocAnnotationsDelete200ResponseMetaAffectedPagesItemWeakAnnotationState([
+ 'kind' => 'known',
+ 'value' => $known,
+ ]);
+ }
+
+ /**
+ * @return bool
+ */
+ public function isUnknown(): bool
+ {
+ return $this->value instanceof DocAnnotationsDelete200ResponseMetaAffectedPagesItemWeakAnnotationStateUnknown && $this->kind === 'unknown';
+ }
+
+ /**
+ * @return DocAnnotationsDelete200ResponseMetaAffectedPagesItemWeakAnnotationStateUnknown
+ */
+ public function asUnknown(): DocAnnotationsDelete200ResponseMetaAffectedPagesItemWeakAnnotationStateUnknown
+ {
+ if (!($this->value instanceof DocAnnotationsDelete200ResponseMetaAffectedPagesItemWeakAnnotationStateUnknown && $this->kind === 'unknown')) {
+ throw new Exception(
+ "Expected unknown; got " . $this->kind . " with value of type " . get_debug_type($this->value),
+ );
+ }
+
+ return $this->value;
+ }
+
+ /**
+ * @return bool
+ */
+ public function isKnown(): bool
+ {
+ return $this->value instanceof DocAnnotationsDelete200ResponseMetaAffectedPagesItemWeakAnnotationStateKnown && $this->kind === 'known';
+ }
+
+ /**
+ * @return DocAnnotationsDelete200ResponseMetaAffectedPagesItemWeakAnnotationStateKnown
+ */
+ public function asKnown(): DocAnnotationsDelete200ResponseMetaAffectedPagesItemWeakAnnotationStateKnown
+ {
+ if (!($this->value instanceof DocAnnotationsDelete200ResponseMetaAffectedPagesItemWeakAnnotationStateKnown && $this->kind === 'known')) {
+ throw new Exception(
+ "Expected known; got " . $this->kind . " with value of type " . get_debug_type($this->value),
+ );
+ }
+
+ return $this->value;
+ }
+
+ /**
+ * @return string
+ */
+ public function __toString(): string
+ {
+ return $this->toJson();
+ }
+
+ /**
+ * @return array
+ */
+ public function jsonSerialize(): array
+ {
+ $result = [];
+ $result['kind'] = $this->kind;
+
+ $base = parent::jsonSerialize();
+ $result = array_merge($base, $result);
+
+ switch ($this->kind) {
+ case 'unknown':
+ $value = $this->asUnknown()->jsonSerialize();
+ $result = array_merge($value, $result);
+ break;
+ case 'known':
+ $value = $this->asKnown()->jsonSerialize();
+ $result = array_merge($value, $result);
+ break;
+ case '_unknown':
+ default:
+ if (is_null($this->value)) {
+ break;
+ }
+ if ($this->value instanceof JsonSerializableType) {
+ $value = $this->value->jsonSerialize();
+ $result = array_merge($value, $result);
+ } elseif (is_array($this->value)) {
+ $result = array_merge($this->value, $result);
+ }
+ }
+
+ return $result;
+ }
+
+ /**
+ * @param array $data
+ */
+ public static function jsonDeserialize(array $data): static
+ {
+ $args = [];
+ if (!array_key_exists('kind', $data)) {
+ throw new Exception(
+ "JSON data is missing property 'kind'",
+ );
+ }
+ $kind = $data['kind'];
+ if (!(is_string($kind))) {
+ throw new Exception(
+ "Expected property 'kind' in JSON data to be string, instead received " . get_debug_type($data['kind']),
+ );
+ }
+
+ $args['kind'] = $kind;
+ switch ($kind) {
+ case 'unknown':
+ $args['value'] = DocAnnotationsDelete200ResponseMetaAffectedPagesItemWeakAnnotationStateUnknown::jsonDeserialize($data);
+ break;
+ case 'known':
+ $args['value'] = DocAnnotationsDelete200ResponseMetaAffectedPagesItemWeakAnnotationStateKnown::jsonDeserialize($data);
+ break;
+ case '_unknown':
+ default:
+ $args['kind'] = '_unknown';
+ $args['value'] = $data;
+ }
+
+ // @phpstan-ignore-next-line
+ return new static($args);
+ }
+}
diff --git a/src/Types/DocAnnotationsDelete200ResponseMetaAffectedPagesItemWeakAnnotationStateKnown.php b/src/Types/DocAnnotationsDelete200ResponseMetaAffectedPagesItemWeakAnnotationStateKnown.php
new file mode 100644
index 0000000..59f6181
--- /dev/null
+++ b/src/Types/DocAnnotationsDelete200ResponseMetaAffectedPagesItemWeakAnnotationStateKnown.php
@@ -0,0 +1,34 @@
+hasAnyWeakAnnotations = $values['hasAnyWeakAnnotations'];
+ }
+
+ /**
+ * @return string
+ */
+ public function __toString(): string
+ {
+ return $this->toJson();
+ }
+}
diff --git a/src/Types/DocAnnotationsDelete200ResponseMetaAffectedPagesItemWeakAnnotationStateUnknown.php b/src/Types/DocAnnotationsDelete200ResponseMetaAffectedPagesItemWeakAnnotationStateUnknown.php
new file mode 100644
index 0000000..fc7e6ff
--- /dev/null
+++ b/src/Types/DocAnnotationsDelete200ResponseMetaAffectedPagesItemWeakAnnotationStateUnknown.php
@@ -0,0 +1,26 @@
+toJson();
+ }
+}
diff --git a/src/Types/DocAnnotationsDelete200ResponseMetaCacheDelta.php b/src/Types/DocAnnotationsDelete200ResponseMetaCacheDelta.php
new file mode 100644
index 0000000..2c62acf
--- /dev/null
+++ b/src/Types/DocAnnotationsDelete200ResponseMetaCacheDelta.php
@@ -0,0 +1,51 @@
+ $pages
+ */
+ #[JsonProperty('pages'), ArrayType([DocAnnotationsDelete200ResponseMetaCacheDeltaPagesItem::class])]
+ public array $pages;
+
+ /**
+ * @param array{
+ * previousDocVersion: int,
+ * docVersion: int,
+ * pages: array,
+ * } $values
+ */
+ public function __construct(
+ array $values,
+ ) {
+ $this->previousDocVersion = $values['previousDocVersion'];
+ $this->docVersion = $values['docVersion'];
+ $this->pages = $values['pages'];
+ }
+
+ /**
+ * @return string
+ */
+ public function __toString(): string
+ {
+ return $this->toJson();
+ }
+}
diff --git a/src/Types/DocAnnotationsDelete200ResponseMetaCacheDeltaPagesItem.php b/src/Types/DocAnnotationsDelete200ResponseMetaCacheDeltaPagesItem.php
new file mode 100644
index 0000000..e442cf5
--- /dev/null
+++ b/src/Types/DocAnnotationsDelete200ResponseMetaCacheDeltaPagesItem.php
@@ -0,0 +1,42 @@
+pageObjectNumber = $values['pageObjectNumber'];
+ $this->cache = $values['cache'];
+ }
+
+ /**
+ * @return string
+ */
+ public function __toString(): string
+ {
+ return $this->toJson();
+ }
+}
diff --git a/src/Types/DocAnnotationsDelete200ResponseMetaCacheDeltaPagesItemCache.php b/src/Types/DocAnnotationsDelete200ResponseMetaCacheDeltaPagesItemCache.php
new file mode 100644
index 0000000..b07c362
--- /dev/null
+++ b/src/Types/DocAnnotationsDelete200ResponseMetaCacheDeltaPagesItemCache.php
@@ -0,0 +1,42 @@
+contentVersion = $values['contentVersion'];
+ $this->annotationVersion = $values['annotationVersion'];
+ }
+
+ /**
+ * @return string
+ */
+ public function __toString(): string
+ {
+ return $this->toJson();
+ }
+}
diff --git a/src/Types/DocAnnotationsDelete404Response.php b/src/Types/DocAnnotationsDelete404Response.php
new file mode 100644
index 0000000..3175bbf
--- /dev/null
+++ b/src/Types/DocAnnotationsDelete404Response.php
@@ -0,0 +1,59 @@
+ $name
+ */
+ #[JsonProperty('name')]
+ public string $name;
+
+ /**
+ * @var value-of $code
+ */
+ #[JsonProperty('code')]
+ public string $code;
+
+ /**
+ * @var string $message
+ */
+ #[JsonProperty('message')]
+ public string $message;
+
+ /**
+ * @var ?array $details
+ */
+ #[JsonProperty('details'), ArrayType(['string' => 'mixed'])]
+ public ?array $details;
+
+ /**
+ * @param array{
+ * name: value-of,
+ * code: value-of,
+ * message: string,
+ * details?: ?array,
+ * } $values
+ */
+ public function __construct(
+ array $values,
+ ) {
+ $this->name = $values['name'];
+ $this->code = $values['code'];
+ $this->message = $values['message'];
+ $this->details = $values['details'] ?? null;
+ }
+
+ /**
+ * @return string
+ */
+ public function __toString(): string
+ {
+ return $this->toJson();
+ }
+}
diff --git a/src/Types/DocAnnotationsDelete404ResponseCode.php b/src/Types/DocAnnotationsDelete404ResponseCode.php
new file mode 100644
index 0000000..05b3b6c
--- /dev/null
+++ b/src/Types/DocAnnotationsDelete404ResponseCode.php
@@ -0,0 +1,25 @@
+ $annotations
+ */
+ #[JsonProperty('annotations'), ArrayType([DocAnnotationsList200ResponseAnnotationsItem::class])]
+ public array $annotations;
+
+ /**
+ * @param array{
+ * pageState: DocAnnotationsList200ResponsePageState,
+ * annotations: array,
+ * } $values
+ */
+ public function __construct(
+ array $values,
+ ) {
+ $this->pageState = $values['pageState'];
+ $this->annotations = $values['annotations'];
+ }
+
+ /**
+ * @return string
+ */
+ public function __toString(): string
+ {
+ return $this->toJson();
+ }
+}
diff --git a/src/Types/DocAnnotationsList200ResponseAnnotationsItem.php b/src/Types/DocAnnotationsList200ResponseAnnotationsItem.php
new file mode 100644
index 0000000..0a7b5af
--- /dev/null
+++ b/src/Types/DocAnnotationsList200ResponseAnnotationsItem.php
@@ -0,0 +1,961 @@
+subtype = $values['subtype'];
+ $this->value = $values['value'];
+ }
+
+ /**
+ * @param DocAnnotationsList200ResponseAnnotationsItemHighlight $highlight
+ * @return DocAnnotationsList200ResponseAnnotationsItem
+ */
+ public static function highlight(DocAnnotationsList200ResponseAnnotationsItemHighlight $highlight): DocAnnotationsList200ResponseAnnotationsItem
+ {
+ return new DocAnnotationsList200ResponseAnnotationsItem([
+ 'subtype' => 'highlight',
+ 'value' => $highlight,
+ ]);
+ }
+
+ /**
+ * @param DocAnnotationsList200ResponseAnnotationsItemUnderline $underline
+ * @return DocAnnotationsList200ResponseAnnotationsItem
+ */
+ public static function underline(DocAnnotationsList200ResponseAnnotationsItemUnderline $underline): DocAnnotationsList200ResponseAnnotationsItem
+ {
+ return new DocAnnotationsList200ResponseAnnotationsItem([
+ 'subtype' => 'underline',
+ 'value' => $underline,
+ ]);
+ }
+
+ /**
+ * @param DocAnnotationsList200ResponseAnnotationsItemSquiggly $squiggly
+ * @return DocAnnotationsList200ResponseAnnotationsItem
+ */
+ public static function squiggly(DocAnnotationsList200ResponseAnnotationsItemSquiggly $squiggly): DocAnnotationsList200ResponseAnnotationsItem
+ {
+ return new DocAnnotationsList200ResponseAnnotationsItem([
+ 'subtype' => 'squiggly',
+ 'value' => $squiggly,
+ ]);
+ }
+
+ /**
+ * @param DocAnnotationsList200ResponseAnnotationsItemStrikeout $strikeout
+ * @return DocAnnotationsList200ResponseAnnotationsItem
+ */
+ public static function strikeout(DocAnnotationsList200ResponseAnnotationsItemStrikeout $strikeout): DocAnnotationsList200ResponseAnnotationsItem
+ {
+ return new DocAnnotationsList200ResponseAnnotationsItem([
+ 'subtype' => 'strikeout',
+ 'value' => $strikeout,
+ ]);
+ }
+
+ /**
+ * @param DocAnnotationsList200ResponseAnnotationsItemCircle $circle
+ * @return DocAnnotationsList200ResponseAnnotationsItem
+ */
+ public static function circle(DocAnnotationsList200ResponseAnnotationsItemCircle $circle): DocAnnotationsList200ResponseAnnotationsItem
+ {
+ return new DocAnnotationsList200ResponseAnnotationsItem([
+ 'subtype' => 'circle',
+ 'value' => $circle,
+ ]);
+ }
+
+ /**
+ * @param DocAnnotationsList200ResponseAnnotationsItemSquare $square
+ * @return DocAnnotationsList200ResponseAnnotationsItem
+ */
+ public static function square(DocAnnotationsList200ResponseAnnotationsItemSquare $square): DocAnnotationsList200ResponseAnnotationsItem
+ {
+ return new DocAnnotationsList200ResponseAnnotationsItem([
+ 'subtype' => 'square',
+ 'value' => $square,
+ ]);
+ }
+
+ /**
+ * @param DocAnnotationsList200ResponseAnnotationsItemPolygon $polygon
+ * @return DocAnnotationsList200ResponseAnnotationsItem
+ */
+ public static function polygon(DocAnnotationsList200ResponseAnnotationsItemPolygon $polygon): DocAnnotationsList200ResponseAnnotationsItem
+ {
+ return new DocAnnotationsList200ResponseAnnotationsItem([
+ 'subtype' => 'polygon',
+ 'value' => $polygon,
+ ]);
+ }
+
+ /**
+ * @param DocAnnotationsList200ResponseAnnotationsItemPolyline $polyline
+ * @return DocAnnotationsList200ResponseAnnotationsItem
+ */
+ public static function polyline(DocAnnotationsList200ResponseAnnotationsItemPolyline $polyline): DocAnnotationsList200ResponseAnnotationsItem
+ {
+ return new DocAnnotationsList200ResponseAnnotationsItem([
+ 'subtype' => 'polyline',
+ 'value' => $polyline,
+ ]);
+ }
+
+ /**
+ * @param DocAnnotationsList200ResponseAnnotationsItemLine $line
+ * @return DocAnnotationsList200ResponseAnnotationsItem
+ */
+ public static function line(DocAnnotationsList200ResponseAnnotationsItemLine $line): DocAnnotationsList200ResponseAnnotationsItem
+ {
+ return new DocAnnotationsList200ResponseAnnotationsItem([
+ 'subtype' => 'line',
+ 'value' => $line,
+ ]);
+ }
+
+ /**
+ * @param DocAnnotationsList200ResponseAnnotationsItemLink $link
+ * @return DocAnnotationsList200ResponseAnnotationsItem
+ */
+ public static function link(DocAnnotationsList200ResponseAnnotationsItemLink $link): DocAnnotationsList200ResponseAnnotationsItem
+ {
+ return new DocAnnotationsList200ResponseAnnotationsItem([
+ 'subtype' => 'link',
+ 'value' => $link,
+ ]);
+ }
+
+ /**
+ * @param DocAnnotationsList200ResponseAnnotationsItemInk $ink
+ * @return DocAnnotationsList200ResponseAnnotationsItem
+ */
+ public static function ink(DocAnnotationsList200ResponseAnnotationsItemInk $ink): DocAnnotationsList200ResponseAnnotationsItem
+ {
+ return new DocAnnotationsList200ResponseAnnotationsItem([
+ 'subtype' => 'ink',
+ 'value' => $ink,
+ ]);
+ }
+
+ /**
+ * @param DocAnnotationsList200ResponseAnnotationsItemFreeText $freeText
+ * @return DocAnnotationsList200ResponseAnnotationsItem
+ */
+ public static function freeText(DocAnnotationsList200ResponseAnnotationsItemFreeText $freeText): DocAnnotationsList200ResponseAnnotationsItem
+ {
+ return new DocAnnotationsList200ResponseAnnotationsItem([
+ 'subtype' => 'free-text',
+ 'value' => $freeText,
+ ]);
+ }
+
+ /**
+ * @param DocAnnotationsList200ResponseAnnotationsItemCaret $caret
+ * @return DocAnnotationsList200ResponseAnnotationsItem
+ */
+ public static function caret(DocAnnotationsList200ResponseAnnotationsItemCaret $caret): DocAnnotationsList200ResponseAnnotationsItem
+ {
+ return new DocAnnotationsList200ResponseAnnotationsItem([
+ 'subtype' => 'caret',
+ 'value' => $caret,
+ ]);
+ }
+
+ /**
+ * @param DocAnnotationsList200ResponseAnnotationsItemText $text
+ * @return DocAnnotationsList200ResponseAnnotationsItem
+ */
+ public static function text(DocAnnotationsList200ResponseAnnotationsItemText $text): DocAnnotationsList200ResponseAnnotationsItem
+ {
+ return new DocAnnotationsList200ResponseAnnotationsItem([
+ 'subtype' => 'text',
+ 'value' => $text,
+ ]);
+ }
+
+ /**
+ * @param DocAnnotationsList200ResponseAnnotationsItemStamp $stamp
+ * @return DocAnnotationsList200ResponseAnnotationsItem
+ */
+ public static function stamp(DocAnnotationsList200ResponseAnnotationsItemStamp $stamp): DocAnnotationsList200ResponseAnnotationsItem
+ {
+ return new DocAnnotationsList200ResponseAnnotationsItem([
+ 'subtype' => 'stamp',
+ 'value' => $stamp,
+ ]);
+ }
+
+ /**
+ * @param DocAnnotationsList200ResponseAnnotationsItemFileAttachment $fileAttachment
+ * @return DocAnnotationsList200ResponseAnnotationsItem
+ */
+ public static function fileAttachment(DocAnnotationsList200ResponseAnnotationsItemFileAttachment $fileAttachment): DocAnnotationsList200ResponseAnnotationsItem
+ {
+ return new DocAnnotationsList200ResponseAnnotationsItem([
+ 'subtype' => 'file-attachment',
+ 'value' => $fileAttachment,
+ ]);
+ }
+
+ /**
+ * @param DocAnnotationsList200ResponseAnnotationsItemWidget $widget
+ * @return DocAnnotationsList200ResponseAnnotationsItem
+ */
+ public static function widget(DocAnnotationsList200ResponseAnnotationsItemWidget $widget): DocAnnotationsList200ResponseAnnotationsItem
+ {
+ return new DocAnnotationsList200ResponseAnnotationsItem([
+ 'subtype' => 'widget',
+ 'value' => $widget,
+ ]);
+ }
+
+ /**
+ * @param DocAnnotationsList200ResponseAnnotationsItemRedact $redact
+ * @return DocAnnotationsList200ResponseAnnotationsItem
+ */
+ public static function redact(DocAnnotationsList200ResponseAnnotationsItemRedact $redact): DocAnnotationsList200ResponseAnnotationsItem
+ {
+ return new DocAnnotationsList200ResponseAnnotationsItem([
+ 'subtype' => 'redact',
+ 'value' => $redact,
+ ]);
+ }
+
+ /**
+ * @param DocAnnotationsList200ResponseAnnotationsItemUnsupported $unsupported
+ * @return DocAnnotationsList200ResponseAnnotationsItem
+ */
+ public static function unsupported(DocAnnotationsList200ResponseAnnotationsItemUnsupported $unsupported): DocAnnotationsList200ResponseAnnotationsItem
+ {
+ return new DocAnnotationsList200ResponseAnnotationsItem([
+ 'subtype' => 'unsupported',
+ 'value' => $unsupported,
+ ]);
+ }
+
+ /**
+ * @return bool
+ */
+ public function isHighlight(): bool
+ {
+ return $this->value instanceof DocAnnotationsList200ResponseAnnotationsItemHighlight && $this->subtype === 'highlight';
+ }
+
+ /**
+ * @return DocAnnotationsList200ResponseAnnotationsItemHighlight
+ */
+ public function asHighlight(): DocAnnotationsList200ResponseAnnotationsItemHighlight
+ {
+ if (!($this->value instanceof DocAnnotationsList200ResponseAnnotationsItemHighlight && $this->subtype === 'highlight')) {
+ throw new Exception(
+ "Expected highlight; got " . $this->subtype . " with value of type " . get_debug_type($this->value),
+ );
+ }
+
+ return $this->value;
+ }
+
+ /**
+ * @return bool
+ */
+ public function isUnderline(): bool
+ {
+ return $this->value instanceof DocAnnotationsList200ResponseAnnotationsItemUnderline && $this->subtype === 'underline';
+ }
+
+ /**
+ * @return DocAnnotationsList200ResponseAnnotationsItemUnderline
+ */
+ public function asUnderline(): DocAnnotationsList200ResponseAnnotationsItemUnderline
+ {
+ if (!($this->value instanceof DocAnnotationsList200ResponseAnnotationsItemUnderline && $this->subtype === 'underline')) {
+ throw new Exception(
+ "Expected underline; got " . $this->subtype . " with value of type " . get_debug_type($this->value),
+ );
+ }
+
+ return $this->value;
+ }
+
+ /**
+ * @return bool
+ */
+ public function isSquiggly(): bool
+ {
+ return $this->value instanceof DocAnnotationsList200ResponseAnnotationsItemSquiggly && $this->subtype === 'squiggly';
+ }
+
+ /**
+ * @return DocAnnotationsList200ResponseAnnotationsItemSquiggly
+ */
+ public function asSquiggly(): DocAnnotationsList200ResponseAnnotationsItemSquiggly
+ {
+ if (!($this->value instanceof DocAnnotationsList200ResponseAnnotationsItemSquiggly && $this->subtype === 'squiggly')) {
+ throw new Exception(
+ "Expected squiggly; got " . $this->subtype . " with value of type " . get_debug_type($this->value),
+ );
+ }
+
+ return $this->value;
+ }
+
+ /**
+ * @return bool
+ */
+ public function isStrikeout(): bool
+ {
+ return $this->value instanceof DocAnnotationsList200ResponseAnnotationsItemStrikeout && $this->subtype === 'strikeout';
+ }
+
+ /**
+ * @return DocAnnotationsList200ResponseAnnotationsItemStrikeout
+ */
+ public function asStrikeout(): DocAnnotationsList200ResponseAnnotationsItemStrikeout
+ {
+ if (!($this->value instanceof DocAnnotationsList200ResponseAnnotationsItemStrikeout && $this->subtype === 'strikeout')) {
+ throw new Exception(
+ "Expected strikeout; got " . $this->subtype . " with value of type " . get_debug_type($this->value),
+ );
+ }
+
+ return $this->value;
+ }
+
+ /**
+ * @return bool
+ */
+ public function isCircle(): bool
+ {
+ return $this->value instanceof DocAnnotationsList200ResponseAnnotationsItemCircle && $this->subtype === 'circle';
+ }
+
+ /**
+ * @return DocAnnotationsList200ResponseAnnotationsItemCircle
+ */
+ public function asCircle(): DocAnnotationsList200ResponseAnnotationsItemCircle
+ {
+ if (!($this->value instanceof DocAnnotationsList200ResponseAnnotationsItemCircle && $this->subtype === 'circle')) {
+ throw new Exception(
+ "Expected circle; got " . $this->subtype . " with value of type " . get_debug_type($this->value),
+ );
+ }
+
+ return $this->value;
+ }
+
+ /**
+ * @return bool
+ */
+ public function isSquare(): bool
+ {
+ return $this->value instanceof DocAnnotationsList200ResponseAnnotationsItemSquare && $this->subtype === 'square';
+ }
+
+ /**
+ * @return DocAnnotationsList200ResponseAnnotationsItemSquare
+ */
+ public function asSquare(): DocAnnotationsList200ResponseAnnotationsItemSquare
+ {
+ if (!($this->value instanceof DocAnnotationsList200ResponseAnnotationsItemSquare && $this->subtype === 'square')) {
+ throw new Exception(
+ "Expected square; got " . $this->subtype . " with value of type " . get_debug_type($this->value),
+ );
+ }
+
+ return $this->value;
+ }
+
+ /**
+ * @return bool
+ */
+ public function isPolygon(): bool
+ {
+ return $this->value instanceof DocAnnotationsList200ResponseAnnotationsItemPolygon && $this->subtype === 'polygon';
+ }
+
+ /**
+ * @return DocAnnotationsList200ResponseAnnotationsItemPolygon
+ */
+ public function asPolygon(): DocAnnotationsList200ResponseAnnotationsItemPolygon
+ {
+ if (!($this->value instanceof DocAnnotationsList200ResponseAnnotationsItemPolygon && $this->subtype === 'polygon')) {
+ throw new Exception(
+ "Expected polygon; got " . $this->subtype . " with value of type " . get_debug_type($this->value),
+ );
+ }
+
+ return $this->value;
+ }
+
+ /**
+ * @return bool
+ */
+ public function isPolyline(): bool
+ {
+ return $this->value instanceof DocAnnotationsList200ResponseAnnotationsItemPolyline && $this->subtype === 'polyline';
+ }
+
+ /**
+ * @return DocAnnotationsList200ResponseAnnotationsItemPolyline
+ */
+ public function asPolyline(): DocAnnotationsList200ResponseAnnotationsItemPolyline
+ {
+ if (!($this->value instanceof DocAnnotationsList200ResponseAnnotationsItemPolyline && $this->subtype === 'polyline')) {
+ throw new Exception(
+ "Expected polyline; got " . $this->subtype . " with value of type " . get_debug_type($this->value),
+ );
+ }
+
+ return $this->value;
+ }
+
+ /**
+ * @return bool
+ */
+ public function isLine(): bool
+ {
+ return $this->value instanceof DocAnnotationsList200ResponseAnnotationsItemLine && $this->subtype === 'line';
+ }
+
+ /**
+ * @return DocAnnotationsList200ResponseAnnotationsItemLine
+ */
+ public function asLine(): DocAnnotationsList200ResponseAnnotationsItemLine
+ {
+ if (!($this->value instanceof DocAnnotationsList200ResponseAnnotationsItemLine && $this->subtype === 'line')) {
+ throw new Exception(
+ "Expected line; got " . $this->subtype . " with value of type " . get_debug_type($this->value),
+ );
+ }
+
+ return $this->value;
+ }
+
+ /**
+ * @return bool
+ */
+ public function isLink(): bool
+ {
+ return $this->value instanceof DocAnnotationsList200ResponseAnnotationsItemLink && $this->subtype === 'link';
+ }
+
+ /**
+ * @return DocAnnotationsList200ResponseAnnotationsItemLink
+ */
+ public function asLink(): DocAnnotationsList200ResponseAnnotationsItemLink
+ {
+ if (!($this->value instanceof DocAnnotationsList200ResponseAnnotationsItemLink && $this->subtype === 'link')) {
+ throw new Exception(
+ "Expected link; got " . $this->subtype . " with value of type " . get_debug_type($this->value),
+ );
+ }
+
+ return $this->value;
+ }
+
+ /**
+ * @return bool
+ */
+ public function isInk(): bool
+ {
+ return $this->value instanceof DocAnnotationsList200ResponseAnnotationsItemInk && $this->subtype === 'ink';
+ }
+
+ /**
+ * @return DocAnnotationsList200ResponseAnnotationsItemInk
+ */
+ public function asInk(): DocAnnotationsList200ResponseAnnotationsItemInk
+ {
+ if (!($this->value instanceof DocAnnotationsList200ResponseAnnotationsItemInk && $this->subtype === 'ink')) {
+ throw new Exception(
+ "Expected ink; got " . $this->subtype . " with value of type " . get_debug_type($this->value),
+ );
+ }
+
+ return $this->value;
+ }
+
+ /**
+ * @return bool
+ */
+ public function isFreeText(): bool
+ {
+ return $this->value instanceof DocAnnotationsList200ResponseAnnotationsItemFreeText && $this->subtype === 'free-text';
+ }
+
+ /**
+ * @return DocAnnotationsList200ResponseAnnotationsItemFreeText
+ */
+ public function asFreeText(): DocAnnotationsList200ResponseAnnotationsItemFreeText
+ {
+ if (!($this->value instanceof DocAnnotationsList200ResponseAnnotationsItemFreeText && $this->subtype === 'free-text')) {
+ throw new Exception(
+ "Expected free-text; got " . $this->subtype . " with value of type " . get_debug_type($this->value),
+ );
+ }
+
+ return $this->value;
+ }
+
+ /**
+ * @return bool
+ */
+ public function isCaret(): bool
+ {
+ return $this->value instanceof DocAnnotationsList200ResponseAnnotationsItemCaret && $this->subtype === 'caret';
+ }
+
+ /**
+ * @return DocAnnotationsList200ResponseAnnotationsItemCaret
+ */
+ public function asCaret(): DocAnnotationsList200ResponseAnnotationsItemCaret
+ {
+ if (!($this->value instanceof DocAnnotationsList200ResponseAnnotationsItemCaret && $this->subtype === 'caret')) {
+ throw new Exception(
+ "Expected caret; got " . $this->subtype . " with value of type " . get_debug_type($this->value),
+ );
+ }
+
+ return $this->value;
+ }
+
+ /**
+ * @return bool
+ */
+ public function isText(): bool
+ {
+ return $this->value instanceof DocAnnotationsList200ResponseAnnotationsItemText && $this->subtype === 'text';
+ }
+
+ /**
+ * @return DocAnnotationsList200ResponseAnnotationsItemText
+ */
+ public function asText(): DocAnnotationsList200ResponseAnnotationsItemText
+ {
+ if (!($this->value instanceof DocAnnotationsList200ResponseAnnotationsItemText && $this->subtype === 'text')) {
+ throw new Exception(
+ "Expected text; got " . $this->subtype . " with value of type " . get_debug_type($this->value),
+ );
+ }
+
+ return $this->value;
+ }
+
+ /**
+ * @return bool
+ */
+ public function isStamp(): bool
+ {
+ return $this->value instanceof DocAnnotationsList200ResponseAnnotationsItemStamp && $this->subtype === 'stamp';
+ }
+
+ /**
+ * @return DocAnnotationsList200ResponseAnnotationsItemStamp
+ */
+ public function asStamp(): DocAnnotationsList200ResponseAnnotationsItemStamp
+ {
+ if (!($this->value instanceof DocAnnotationsList200ResponseAnnotationsItemStamp && $this->subtype === 'stamp')) {
+ throw new Exception(
+ "Expected stamp; got " . $this->subtype . " with value of type " . get_debug_type($this->value),
+ );
+ }
+
+ return $this->value;
+ }
+
+ /**
+ * @return bool
+ */
+ public function isFileAttachment(): bool
+ {
+ return $this->value instanceof DocAnnotationsList200ResponseAnnotationsItemFileAttachment && $this->subtype === 'file-attachment';
+ }
+
+ /**
+ * @return DocAnnotationsList200ResponseAnnotationsItemFileAttachment
+ */
+ public function asFileAttachment(): DocAnnotationsList200ResponseAnnotationsItemFileAttachment
+ {
+ if (!($this->value instanceof DocAnnotationsList200ResponseAnnotationsItemFileAttachment && $this->subtype === 'file-attachment')) {
+ throw new Exception(
+ "Expected file-attachment; got " . $this->subtype . " with value of type " . get_debug_type($this->value),
+ );
+ }
+
+ return $this->value;
+ }
+
+ /**
+ * @return bool
+ */
+ public function isWidget(): bool
+ {
+ return $this->value instanceof DocAnnotationsList200ResponseAnnotationsItemWidget && $this->subtype === 'widget';
+ }
+
+ /**
+ * @return DocAnnotationsList200ResponseAnnotationsItemWidget
+ */
+ public function asWidget(): DocAnnotationsList200ResponseAnnotationsItemWidget
+ {
+ if (!($this->value instanceof DocAnnotationsList200ResponseAnnotationsItemWidget && $this->subtype === 'widget')) {
+ throw new Exception(
+ "Expected widget; got " . $this->subtype . " with value of type " . get_debug_type($this->value),
+ );
+ }
+
+ return $this->value;
+ }
+
+ /**
+ * @return bool
+ */
+ public function isRedact(): bool
+ {
+ return $this->value instanceof DocAnnotationsList200ResponseAnnotationsItemRedact && $this->subtype === 'redact';
+ }
+
+ /**
+ * @return DocAnnotationsList200ResponseAnnotationsItemRedact
+ */
+ public function asRedact(): DocAnnotationsList200ResponseAnnotationsItemRedact
+ {
+ if (!($this->value instanceof DocAnnotationsList200ResponseAnnotationsItemRedact && $this->subtype === 'redact')) {
+ throw new Exception(
+ "Expected redact; got " . $this->subtype . " with value of type " . get_debug_type($this->value),
+ );
+ }
+
+ return $this->value;
+ }
+
+ /**
+ * @return bool
+ */
+ public function isUnsupported(): bool
+ {
+ return $this->value instanceof DocAnnotationsList200ResponseAnnotationsItemUnsupported && $this->subtype === 'unsupported';
+ }
+
+ /**
+ * @return DocAnnotationsList200ResponseAnnotationsItemUnsupported
+ */
+ public function asUnsupported(): DocAnnotationsList200ResponseAnnotationsItemUnsupported
+ {
+ if (!($this->value instanceof DocAnnotationsList200ResponseAnnotationsItemUnsupported && $this->subtype === 'unsupported')) {
+ throw new Exception(
+ "Expected unsupported; got " . $this->subtype . " with value of type " . get_debug_type($this->value),
+ );
+ }
+
+ return $this->value;
+ }
+
+ /**
+ * @return string
+ */
+ public function __toString(): string
+ {
+ return $this->toJson();
+ }
+
+ /**
+ * @return array
+ */
+ public function jsonSerialize(): array
+ {
+ $result = [];
+ $result['subtype'] = $this->subtype;
+
+ $base = parent::jsonSerialize();
+ $result = array_merge($base, $result);
+
+ switch ($this->subtype) {
+ case 'highlight':
+ $value = $this->asHighlight()->jsonSerialize();
+ $result = array_merge($value, $result);
+ break;
+ case 'underline':
+ $value = $this->asUnderline()->jsonSerialize();
+ $result = array_merge($value, $result);
+ break;
+ case 'squiggly':
+ $value = $this->asSquiggly()->jsonSerialize();
+ $result = array_merge($value, $result);
+ break;
+ case 'strikeout':
+ $value = $this->asStrikeout()->jsonSerialize();
+ $result = array_merge($value, $result);
+ break;
+ case 'circle':
+ $value = $this->asCircle()->jsonSerialize();
+ $result = array_merge($value, $result);
+ break;
+ case 'square':
+ $value = $this->asSquare()->jsonSerialize();
+ $result = array_merge($value, $result);
+ break;
+ case 'polygon':
+ $value = $this->asPolygon()->jsonSerialize();
+ $result = array_merge($value, $result);
+ break;
+ case 'polyline':
+ $value = $this->asPolyline()->jsonSerialize();
+ $result = array_merge($value, $result);
+ break;
+ case 'line':
+ $value = $this->asLine()->jsonSerialize();
+ $result = array_merge($value, $result);
+ break;
+ case 'link':
+ $value = $this->asLink()->jsonSerialize();
+ $result = array_merge($value, $result);
+ break;
+ case 'ink':
+ $value = $this->asInk()->jsonSerialize();
+ $result = array_merge($value, $result);
+ break;
+ case 'free-text':
+ $value = $this->asFreeText()->jsonSerialize();
+ $result = array_merge($value, $result);
+ break;
+ case 'caret':
+ $value = $this->asCaret()->jsonSerialize();
+ $result = array_merge($value, $result);
+ break;
+ case 'text':
+ $value = $this->asText()->jsonSerialize();
+ $result = array_merge($value, $result);
+ break;
+ case 'stamp':
+ $value = $this->asStamp()->jsonSerialize();
+ $result = array_merge($value, $result);
+ break;
+ case 'file-attachment':
+ $value = $this->asFileAttachment()->jsonSerialize();
+ $result = array_merge($value, $result);
+ break;
+ case 'widget':
+ $value = $this->asWidget()->jsonSerialize();
+ $result = array_merge($value, $result);
+ break;
+ case 'redact':
+ $value = $this->asRedact()->jsonSerialize();
+ $result = array_merge($value, $result);
+ break;
+ case 'unsupported':
+ $value = $this->asUnsupported()->jsonSerialize();
+ $result = array_merge($value, $result);
+ break;
+ case '_unknown':
+ default:
+ if (is_null($this->value)) {
+ break;
+ }
+ if ($this->value instanceof JsonSerializableType) {
+ $value = $this->value->jsonSerialize();
+ $result = array_merge($value, $result);
+ } elseif (is_array($this->value)) {
+ $result = array_merge($this->value, $result);
+ }
+ }
+
+ return $result;
+ }
+
+ /**
+ * @param array $data
+ */
+ public static function jsonDeserialize(array $data): static
+ {
+ $args = [];
+ if (!array_key_exists('subtype', $data)) {
+ throw new Exception(
+ "JSON data is missing property 'subtype'",
+ );
+ }
+ $subtype = $data['subtype'];
+ if (!(is_string($subtype))) {
+ throw new Exception(
+ "Expected property 'subtype' in JSON data to be string, instead received " . get_debug_type($data['subtype']),
+ );
+ }
+
+ $args['subtype'] = $subtype;
+ switch ($subtype) {
+ case 'highlight':
+ $args['value'] = DocAnnotationsList200ResponseAnnotationsItemHighlight::jsonDeserialize($data);
+ break;
+ case 'underline':
+ $args['value'] = DocAnnotationsList200ResponseAnnotationsItemUnderline::jsonDeserialize($data);
+ break;
+ case 'squiggly':
+ $args['value'] = DocAnnotationsList200ResponseAnnotationsItemSquiggly::jsonDeserialize($data);
+ break;
+ case 'strikeout':
+ $args['value'] = DocAnnotationsList200ResponseAnnotationsItemStrikeout::jsonDeserialize($data);
+ break;
+ case 'circle':
+ $args['value'] = DocAnnotationsList200ResponseAnnotationsItemCircle::jsonDeserialize($data);
+ break;
+ case 'square':
+ $args['value'] = DocAnnotationsList200ResponseAnnotationsItemSquare::jsonDeserialize($data);
+ break;
+ case 'polygon':
+ $args['value'] = DocAnnotationsList200ResponseAnnotationsItemPolygon::jsonDeserialize($data);
+ break;
+ case 'polyline':
+ $args['value'] = DocAnnotationsList200ResponseAnnotationsItemPolyline::jsonDeserialize($data);
+ break;
+ case 'line':
+ $args['value'] = DocAnnotationsList200ResponseAnnotationsItemLine::jsonDeserialize($data);
+ break;
+ case 'link':
+ $args['value'] = DocAnnotationsList200ResponseAnnotationsItemLink::jsonDeserialize($data);
+ break;
+ case 'ink':
+ $args['value'] = DocAnnotationsList200ResponseAnnotationsItemInk::jsonDeserialize($data);
+ break;
+ case 'free-text':
+ $args['value'] = DocAnnotationsList200ResponseAnnotationsItemFreeText::jsonDeserialize($data);
+ break;
+ case 'caret':
+ $args['value'] = DocAnnotationsList200ResponseAnnotationsItemCaret::jsonDeserialize($data);
+ break;
+ case 'text':
+ $args['value'] = DocAnnotationsList200ResponseAnnotationsItemText::jsonDeserialize($data);
+ break;
+ case 'stamp':
+ $args['value'] = DocAnnotationsList200ResponseAnnotationsItemStamp::jsonDeserialize($data);
+ break;
+ case 'file-attachment':
+ $args['value'] = DocAnnotationsList200ResponseAnnotationsItemFileAttachment::jsonDeserialize($data);
+ break;
+ case 'widget':
+ $args['value'] = DocAnnotationsList200ResponseAnnotationsItemWidget::jsonDeserialize($data);
+ break;
+ case 'redact':
+ $args['value'] = DocAnnotationsList200ResponseAnnotationsItemRedact::jsonDeserialize($data);
+ break;
+ case 'unsupported':
+ $args['value'] = DocAnnotationsList200ResponseAnnotationsItemUnsupported::jsonDeserialize($data);
+ break;
+ case '_unknown':
+ default:
+ $args['subtype'] = '_unknown';
+ $args['value'] = $data;
+ }
+
+ // @phpstan-ignore-next-line
+ return new static($args);
+ }
+}
diff --git a/src/Types/DocAnnotationsList200ResponseAnnotationsItemCaret.php b/src/Types/DocAnnotationsList200ResponseAnnotationsItemCaret.php
new file mode 100644
index 0000000..b2e9d57
--- /dev/null
+++ b/src/Types/DocAnnotationsList200ResponseAnnotationsItemCaret.php
@@ -0,0 +1,212 @@
+ $identityQuality
+ */
+ #[JsonProperty('identityQuality')]
+ public string $identityQuality;
+
+ /**
+ * @var ?string $nm
+ */
+ #[JsonProperty('nm')]
+ public ?string $nm;
+
+ /**
+ * @var DocAnnotationsList200ResponseAnnotationsItemCaretFlags $flags
+ */
+ #[JsonProperty('flags')]
+ public DocAnnotationsList200ResponseAnnotationsItemCaretFlags $flags;
+
+ /**
+ * @var DocAnnotationsList200ResponseAnnotationsItemCaretRect $rect
+ */
+ #[JsonProperty('rect')]
+ public DocAnnotationsList200ResponseAnnotationsItemCaretRect $rect;
+
+ /**
+ * @var ?string $contents
+ */
+ #[JsonProperty('contents')]
+ public ?string $contents;
+
+ /**
+ * @var ?string $author
+ */
+ #[JsonProperty('author')]
+ public ?string $author;
+
+ /**
+ * @var ?DateTime $created
+ */
+ #[JsonProperty('created'), Date(Date::TYPE_DATETIME)]
+ public ?DateTime $created;
+
+ /**
+ * @var ?DateTime $modified
+ */
+ #[JsonProperty('modified'), Date(Date::TYPE_DATETIME)]
+ public ?DateTime $modified;
+
+ /**
+ * @var value-of $blendMode
+ */
+ #[JsonProperty('blendMode')]
+ public string $blendMode;
+
+ /**
+ * @var ?DocAnnotationsList200ResponseAnnotationsItemCaretInReplyTo $inReplyTo
+ */
+ #[JsonProperty('inReplyTo')]
+ public ?DocAnnotationsList200ResponseAnnotationsItemCaretInReplyTo $inReplyTo;
+
+ /**
+ * @var ?value-of $replyType
+ */
+ #[JsonProperty('replyType')]
+ public ?string $replyType;
+
+ /**
+ * @var ?string $userId
+ */
+ #[JsonProperty('userId')]
+ public ?string $userId;
+
+ /**
+ * @var ?string $groupId
+ */
+ #[JsonProperty('groupId')]
+ public ?string $groupId;
+
+ /**
+ * @var ?string $createdBy
+ */
+ #[JsonProperty('createdBy')]
+ public ?string $createdBy;
+
+ /**
+ * @var ?string $updatedBy
+ */
+ #[JsonProperty('updatedBy')]
+ public ?string $updatedBy;
+
+ /**
+ * @var ?DocAnnotationsList200ResponseAnnotationsItemCaretActions $actions
+ */
+ #[JsonProperty('actions')]
+ public ?DocAnnotationsList200ResponseAnnotationsItemCaretActions $actions;
+
+ /**
+ * @var DocAnnotationsList200ResponseAnnotationsItemCaretColor $color
+ */
+ #[JsonProperty('color')]
+ public DocAnnotationsList200ResponseAnnotationsItemCaretColor $color;
+
+ /**
+ * @var float $opacity
+ */
+ #[JsonProperty('opacity')]
+ public float $opacity;
+
+ /**
+ * @var ?value-of $intent
+ */
+ #[JsonProperty('intent')]
+ public ?string $intent;
+
+ /**
+ * @var ?DocAnnotationsList200ResponseAnnotationsItemCaretRectDifferences $rectDifferences
+ */
+ #[JsonProperty('rectDifferences')]
+ public ?DocAnnotationsList200ResponseAnnotationsItemCaretRectDifferences $rectDifferences;
+
+ /**
+ * @param array{
+ * ref: DocAnnotationsList200ResponseAnnotationsItemCaretRef,
+ * pageObjectNumber: int,
+ * index: int,
+ * identityQuality: value-of,
+ * flags: DocAnnotationsList200ResponseAnnotationsItemCaretFlags,
+ * rect: DocAnnotationsList200ResponseAnnotationsItemCaretRect,
+ * blendMode: value-of,
+ * color: DocAnnotationsList200ResponseAnnotationsItemCaretColor,
+ * opacity: float,
+ * nm?: ?string,
+ * contents?: ?string,
+ * author?: ?string,
+ * created?: ?DateTime,
+ * modified?: ?DateTime,
+ * inReplyTo?: ?DocAnnotationsList200ResponseAnnotationsItemCaretInReplyTo,
+ * replyType?: ?value-of,
+ * userId?: ?string,
+ * groupId?: ?string,
+ * createdBy?: ?string,
+ * updatedBy?: ?string,
+ * actions?: ?DocAnnotationsList200ResponseAnnotationsItemCaretActions,
+ * intent?: ?value-of,
+ * rectDifferences?: ?DocAnnotationsList200ResponseAnnotationsItemCaretRectDifferences,
+ * } $values
+ */
+ public function __construct(
+ array $values,
+ ) {
+ $this->ref = $values['ref'];
+ $this->pageObjectNumber = $values['pageObjectNumber'];
+ $this->index = $values['index'];
+ $this->identityQuality = $values['identityQuality'];
+ $this->nm = $values['nm'] ?? null;
+ $this->flags = $values['flags'];
+ $this->rect = $values['rect'];
+ $this->contents = $values['contents'] ?? null;
+ $this->author = $values['author'] ?? null;
+ $this->created = $values['created'] ?? null;
+ $this->modified = $values['modified'] ?? null;
+ $this->blendMode = $values['blendMode'];
+ $this->inReplyTo = $values['inReplyTo'] ?? null;
+ $this->replyType = $values['replyType'] ?? null;
+ $this->userId = $values['userId'] ?? null;
+ $this->groupId = $values['groupId'] ?? null;
+ $this->createdBy = $values['createdBy'] ?? null;
+ $this->updatedBy = $values['updatedBy'] ?? null;
+ $this->actions = $values['actions'] ?? null;
+ $this->color = $values['color'];
+ $this->opacity = $values['opacity'];
+ $this->intent = $values['intent'] ?? null;
+ $this->rectDifferences = $values['rectDifferences'] ?? null;
+ }
+
+ /**
+ * @return string
+ */
+ public function __toString(): string
+ {
+ return $this->toJson();
+ }
+}
diff --git a/src/Types/DocAnnotationsList200ResponseAnnotationsItemCaretActions.php b/src/Types/DocAnnotationsList200ResponseAnnotationsItemCaretActions.php
new file mode 100644
index 0000000..17d0f68
--- /dev/null
+++ b/src/Types/DocAnnotationsList200ResponseAnnotationsItemCaretActions.php
@@ -0,0 +1,114 @@
+activate = $values['activate'] ?? null;
+ $this->cursorEnter = $values['cursorEnter'] ?? null;
+ $this->cursorExit = $values['cursorExit'] ?? null;
+ $this->mouseDown = $values['mouseDown'] ?? null;
+ $this->mouseUp = $values['mouseUp'] ?? null;
+ $this->focus = $values['focus'] ?? null;
+ $this->blur = $values['blur'] ?? null;
+ $this->pageOpen = $values['pageOpen'] ?? null;
+ $this->pageClose = $values['pageClose'] ?? null;
+ $this->pageVisible = $values['pageVisible'] ?? null;
+ $this->pageInvisible = $values['pageInvisible'] ?? null;
+ }
+
+ /**
+ * @return string
+ */
+ public function __toString(): string
+ {
+ return $this->toJson();
+ }
+}
diff --git a/src/Types/DocAnnotationsList200ResponseAnnotationsItemCaretActionsActivate.php b/src/Types/DocAnnotationsList200ResponseAnnotationsItemCaretActionsActivate.php
new file mode 100644
index 0000000..2286d4d
--- /dev/null
+++ b/src/Types/DocAnnotationsList200ResponseAnnotationsItemCaretActionsActivate.php
@@ -0,0 +1,59 @@
+> $warnings
+ */
+ #[JsonProperty('warnings'), ArrayType(['string'])]
+ public array $warnings;
+
+ /**
+ * @param array{
+ * incomplete: bool,
+ * warningFlags: int,
+ * warnings: array>,
+ * root?: ?DocAnnotationsList200ResponseAnnotationsItemCaretActionsActivateRoot,
+ * } $values
+ */
+ public function __construct(
+ array $values,
+ ) {
+ $this->root = $values['root'] ?? null;
+ $this->incomplete = $values['incomplete'];
+ $this->warningFlags = $values['warningFlags'];
+ $this->warnings = $values['warnings'];
+ }
+
+ /**
+ * @return string
+ */
+ public function __toString(): string
+ {
+ return $this->toJson();
+ }
+}
diff --git a/src/Types/DocAnnotationsList200ResponseAnnotationsItemCaretActionsActivateRoot.php b/src/Types/DocAnnotationsList200ResponseAnnotationsItemCaretActionsActivateRoot.php
new file mode 100644
index 0000000..7e2a703
--- /dev/null
+++ b/src/Types/DocAnnotationsList200ResponseAnnotationsItemCaretActionsActivateRoot.php
@@ -0,0 +1,59 @@
+ $type
+ */
+ #[JsonProperty('type')]
+ public string $type;
+
+ /**
+ * @var string $subtype
+ */
+ #[JsonProperty('subtype')]
+ public string $subtype;
+
+ /**
+ * @var ?string $script
+ */
+ #[JsonProperty('script')]
+ public ?string $script;
+
+ /**
+ * @var array $next
+ */
+ #[JsonProperty('next'), ArrayType(['mixed'])]
+ public array $next;
+
+ /**
+ * @param array{
+ * type: value-of,
+ * subtype: string,
+ * next: array,
+ * script?: ?string,
+ * } $values
+ */
+ public function __construct(
+ array $values,
+ ) {
+ $this->type = $values['type'];
+ $this->subtype = $values['subtype'];
+ $this->script = $values['script'] ?? null;
+ $this->next = $values['next'];
+ }
+
+ /**
+ * @return string
+ */
+ public function __toString(): string
+ {
+ return $this->toJson();
+ }
+}
diff --git a/src/Types/DocAnnotationsList200ResponseAnnotationsItemCaretActionsActivateRootType.php b/src/Types/DocAnnotationsList200ResponseAnnotationsItemCaretActionsActivateRootType.php
new file mode 100644
index 0000000..b71de46
--- /dev/null
+++ b/src/Types/DocAnnotationsList200ResponseAnnotationsItemCaretActionsActivateRootType.php
@@ -0,0 +1,26 @@
+> $warnings
+ */
+ #[JsonProperty('warnings'), ArrayType(['string'])]
+ public array $warnings;
+
+ /**
+ * @param array{
+ * incomplete: bool,
+ * warningFlags: int,
+ * warnings: array>,
+ * root?: ?DocAnnotationsList200ResponseAnnotationsItemCaretActionsBlurRoot,
+ * } $values
+ */
+ public function __construct(
+ array $values,
+ ) {
+ $this->root = $values['root'] ?? null;
+ $this->incomplete = $values['incomplete'];
+ $this->warningFlags = $values['warningFlags'];
+ $this->warnings = $values['warnings'];
+ }
+
+ /**
+ * @return string
+ */
+ public function __toString(): string
+ {
+ return $this->toJson();
+ }
+}
diff --git a/src/Types/DocAnnotationsList200ResponseAnnotationsItemCaretActionsBlurRoot.php b/src/Types/DocAnnotationsList200ResponseAnnotationsItemCaretActionsBlurRoot.php
new file mode 100644
index 0000000..95aa932
--- /dev/null
+++ b/src/Types/DocAnnotationsList200ResponseAnnotationsItemCaretActionsBlurRoot.php
@@ -0,0 +1,59 @@
+ $type
+ */
+ #[JsonProperty('type')]
+ public string $type;
+
+ /**
+ * @var string $subtype
+ */
+ #[JsonProperty('subtype')]
+ public string $subtype;
+
+ /**
+ * @var ?string $script
+ */
+ #[JsonProperty('script')]
+ public ?string $script;
+
+ /**
+ * @var array $next
+ */
+ #[JsonProperty('next'), ArrayType(['mixed'])]
+ public array $next;
+
+ /**
+ * @param array{
+ * type: value-of,
+ * subtype: string,
+ * next: array,
+ * script?: ?string,
+ * } $values
+ */
+ public function __construct(
+ array $values,
+ ) {
+ $this->type = $values['type'];
+ $this->subtype = $values['subtype'];
+ $this->script = $values['script'] ?? null;
+ $this->next = $values['next'];
+ }
+
+ /**
+ * @return string
+ */
+ public function __toString(): string
+ {
+ return $this->toJson();
+ }
+}
diff --git a/src/Types/DocAnnotationsList200ResponseAnnotationsItemCaretActionsBlurRootType.php b/src/Types/DocAnnotationsList200ResponseAnnotationsItemCaretActionsBlurRootType.php
new file mode 100644
index 0000000..22b31a0
--- /dev/null
+++ b/src/Types/DocAnnotationsList200ResponseAnnotationsItemCaretActionsBlurRootType.php
@@ -0,0 +1,26 @@
+> $warnings
+ */
+ #[JsonProperty('warnings'), ArrayType(['string'])]
+ public array $warnings;
+
+ /**
+ * @param array{
+ * incomplete: bool,
+ * warningFlags: int,
+ * warnings: array>,
+ * root?: ?DocAnnotationsList200ResponseAnnotationsItemCaretActionsCursorEnterRoot,
+ * } $values
+ */
+ public function __construct(
+ array $values,
+ ) {
+ $this->root = $values['root'] ?? null;
+ $this->incomplete = $values['incomplete'];
+ $this->warningFlags = $values['warningFlags'];
+ $this->warnings = $values['warnings'];
+ }
+
+ /**
+ * @return string
+ */
+ public function __toString(): string
+ {
+ return $this->toJson();
+ }
+}
diff --git a/src/Types/DocAnnotationsList200ResponseAnnotationsItemCaretActionsCursorEnterRoot.php b/src/Types/DocAnnotationsList200ResponseAnnotationsItemCaretActionsCursorEnterRoot.php
new file mode 100644
index 0000000..51f2042
--- /dev/null
+++ b/src/Types/DocAnnotationsList200ResponseAnnotationsItemCaretActionsCursorEnterRoot.php
@@ -0,0 +1,59 @@
+ $type
+ */
+ #[JsonProperty('type')]
+ public string $type;
+
+ /**
+ * @var string $subtype
+ */
+ #[JsonProperty('subtype')]
+ public string $subtype;
+
+ /**
+ * @var ?string $script
+ */
+ #[JsonProperty('script')]
+ public ?string $script;
+
+ /**
+ * @var array $next
+ */
+ #[JsonProperty('next'), ArrayType(['mixed'])]
+ public array $next;
+
+ /**
+ * @param array{
+ * type: value-of,
+ * subtype: string,
+ * next: array,
+ * script?: ?string,
+ * } $values
+ */
+ public function __construct(
+ array $values,
+ ) {
+ $this->type = $values['type'];
+ $this->subtype = $values['subtype'];
+ $this->script = $values['script'] ?? null;
+ $this->next = $values['next'];
+ }
+
+ /**
+ * @return string
+ */
+ public function __toString(): string
+ {
+ return $this->toJson();
+ }
+}
diff --git a/src/Types/DocAnnotationsList200ResponseAnnotationsItemCaretActionsCursorEnterRootType.php b/src/Types/DocAnnotationsList200ResponseAnnotationsItemCaretActionsCursorEnterRootType.php
new file mode 100644
index 0000000..e76b06b
--- /dev/null
+++ b/src/Types/DocAnnotationsList200ResponseAnnotationsItemCaretActionsCursorEnterRootType.php
@@ -0,0 +1,26 @@
+> $warnings
+ */
+ #[JsonProperty('warnings'), ArrayType(['string'])]
+ public array $warnings;
+
+ /**
+ * @param array{
+ * incomplete: bool,
+ * warningFlags: int,
+ * warnings: array>,
+ * root?: ?DocAnnotationsList200ResponseAnnotationsItemCaretActionsCursorExitRoot,
+ * } $values
+ */
+ public function __construct(
+ array $values,
+ ) {
+ $this->root = $values['root'] ?? null;
+ $this->incomplete = $values['incomplete'];
+ $this->warningFlags = $values['warningFlags'];
+ $this->warnings = $values['warnings'];
+ }
+
+ /**
+ * @return string
+ */
+ public function __toString(): string
+ {
+ return $this->toJson();
+ }
+}
diff --git a/src/Types/DocAnnotationsList200ResponseAnnotationsItemCaretActionsCursorExitRoot.php b/src/Types/DocAnnotationsList200ResponseAnnotationsItemCaretActionsCursorExitRoot.php
new file mode 100644
index 0000000..c64d09e
--- /dev/null
+++ b/src/Types/DocAnnotationsList200ResponseAnnotationsItemCaretActionsCursorExitRoot.php
@@ -0,0 +1,59 @@
+ $type
+ */
+ #[JsonProperty('type')]
+ public string $type;
+
+ /**
+ * @var string $subtype
+ */
+ #[JsonProperty('subtype')]
+ public string $subtype;
+
+ /**
+ * @var ?string $script
+ */
+ #[JsonProperty('script')]
+ public ?string $script;
+
+ /**
+ * @var array $next
+ */
+ #[JsonProperty('next'), ArrayType(['mixed'])]
+ public array $next;
+
+ /**
+ * @param array{
+ * type: value-of,
+ * subtype: string,
+ * next: array,
+ * script?: ?string,
+ * } $values
+ */
+ public function __construct(
+ array $values,
+ ) {
+ $this->type = $values['type'];
+ $this->subtype = $values['subtype'];
+ $this->script = $values['script'] ?? null;
+ $this->next = $values['next'];
+ }
+
+ /**
+ * @return string
+ */
+ public function __toString(): string
+ {
+ return $this->toJson();
+ }
+}
diff --git a/src/Types/DocAnnotationsList200ResponseAnnotationsItemCaretActionsCursorExitRootType.php b/src/Types/DocAnnotationsList200ResponseAnnotationsItemCaretActionsCursorExitRootType.php
new file mode 100644
index 0000000..634ed1e
--- /dev/null
+++ b/src/Types/DocAnnotationsList200ResponseAnnotationsItemCaretActionsCursorExitRootType.php
@@ -0,0 +1,26 @@
+> $warnings
+ */
+ #[JsonProperty('warnings'), ArrayType(['string'])]
+ public array $warnings;
+
+ /**
+ * @param array{
+ * incomplete: bool,
+ * warningFlags: int,
+ * warnings: array>,
+ * root?: ?DocAnnotationsList200ResponseAnnotationsItemCaretActionsFocusRoot,
+ * } $values
+ */
+ public function __construct(
+ array $values,
+ ) {
+ $this->root = $values['root'] ?? null;
+ $this->incomplete = $values['incomplete'];
+ $this->warningFlags = $values['warningFlags'];
+ $this->warnings = $values['warnings'];
+ }
+
+ /**
+ * @return string
+ */
+ public function __toString(): string
+ {
+ return $this->toJson();
+ }
+}
diff --git a/src/Types/DocAnnotationsList200ResponseAnnotationsItemCaretActionsFocusRoot.php b/src/Types/DocAnnotationsList200ResponseAnnotationsItemCaretActionsFocusRoot.php
new file mode 100644
index 0000000..f5d80cd
--- /dev/null
+++ b/src/Types/DocAnnotationsList200ResponseAnnotationsItemCaretActionsFocusRoot.php
@@ -0,0 +1,59 @@
+ $type
+ */
+ #[JsonProperty('type')]
+ public string $type;
+
+ /**
+ * @var string $subtype
+ */
+ #[JsonProperty('subtype')]
+ public string $subtype;
+
+ /**
+ * @var ?string $script
+ */
+ #[JsonProperty('script')]
+ public ?string $script;
+
+ /**
+ * @var array $next
+ */
+ #[JsonProperty('next'), ArrayType(['mixed'])]
+ public array $next;
+
+ /**
+ * @param array{
+ * type: value-of,
+ * subtype: string,
+ * next: array,
+ * script?: ?string,
+ * } $values
+ */
+ public function __construct(
+ array $values,
+ ) {
+ $this->type = $values['type'];
+ $this->subtype = $values['subtype'];
+ $this->script = $values['script'] ?? null;
+ $this->next = $values['next'];
+ }
+
+ /**
+ * @return string
+ */
+ public function __toString(): string
+ {
+ return $this->toJson();
+ }
+}
diff --git a/src/Types/DocAnnotationsList200ResponseAnnotationsItemCaretActionsFocusRootType.php b/src/Types/DocAnnotationsList200ResponseAnnotationsItemCaretActionsFocusRootType.php
new file mode 100644
index 0000000..4cb7706
--- /dev/null
+++ b/src/Types/DocAnnotationsList200ResponseAnnotationsItemCaretActionsFocusRootType.php
@@ -0,0 +1,26 @@
+> $warnings
+ */
+ #[JsonProperty('warnings'), ArrayType(['string'])]
+ public array $warnings;
+
+ /**
+ * @param array{
+ * incomplete: bool,
+ * warningFlags: int,
+ * warnings: array>,
+ * root?: ?DocAnnotationsList200ResponseAnnotationsItemCaretActionsMouseDownRoot,
+ * } $values
+ */
+ public function __construct(
+ array $values,
+ ) {
+ $this->root = $values['root'] ?? null;
+ $this->incomplete = $values['incomplete'];
+ $this->warningFlags = $values['warningFlags'];
+ $this->warnings = $values['warnings'];
+ }
+
+ /**
+ * @return string
+ */
+ public function __toString(): string
+ {
+ return $this->toJson();
+ }
+}
diff --git a/src/Types/DocAnnotationsList200ResponseAnnotationsItemCaretActionsMouseDownRoot.php b/src/Types/DocAnnotationsList200ResponseAnnotationsItemCaretActionsMouseDownRoot.php
new file mode 100644
index 0000000..d801521
--- /dev/null
+++ b/src/Types/DocAnnotationsList200ResponseAnnotationsItemCaretActionsMouseDownRoot.php
@@ -0,0 +1,59 @@
+ $type
+ */
+ #[JsonProperty('type')]
+ public string $type;
+
+ /**
+ * @var string $subtype
+ */
+ #[JsonProperty('subtype')]
+ public string $subtype;
+
+ /**
+ * @var ?string $script
+ */
+ #[JsonProperty('script')]
+ public ?string $script;
+
+ /**
+ * @var array $next
+ */
+ #[JsonProperty('next'), ArrayType(['mixed'])]
+ public array $next;
+
+ /**
+ * @param array{
+ * type: value-of,
+ * subtype: string,
+ * next: array,
+ * script?: ?string,
+ * } $values
+ */
+ public function __construct(
+ array $values,
+ ) {
+ $this->type = $values['type'];
+ $this->subtype = $values['subtype'];
+ $this->script = $values['script'] ?? null;
+ $this->next = $values['next'];
+ }
+
+ /**
+ * @return string
+ */
+ public function __toString(): string
+ {
+ return $this->toJson();
+ }
+}
diff --git a/src/Types/DocAnnotationsList200ResponseAnnotationsItemCaretActionsMouseDownRootType.php b/src/Types/DocAnnotationsList200ResponseAnnotationsItemCaretActionsMouseDownRootType.php
new file mode 100644
index 0000000..35d57b2
--- /dev/null
+++ b/src/Types/DocAnnotationsList200ResponseAnnotationsItemCaretActionsMouseDownRootType.php
@@ -0,0 +1,26 @@
+> $warnings
+ */
+ #[JsonProperty('warnings'), ArrayType(['string'])]
+ public array $warnings;
+
+ /**
+ * @param array{
+ * incomplete: bool,
+ * warningFlags: int,
+ * warnings: array>,
+ * root?: ?DocAnnotationsList200ResponseAnnotationsItemCaretActionsMouseUpRoot,
+ * } $values
+ */
+ public function __construct(
+ array $values,
+ ) {
+ $this->root = $values['root'] ?? null;
+ $this->incomplete = $values['incomplete'];
+ $this->warningFlags = $values['warningFlags'];
+ $this->warnings = $values['warnings'];
+ }
+
+ /**
+ * @return string
+ */
+ public function __toString(): string
+ {
+ return $this->toJson();
+ }
+}
diff --git a/src/Types/DocAnnotationsList200ResponseAnnotationsItemCaretActionsMouseUpRoot.php b/src/Types/DocAnnotationsList200ResponseAnnotationsItemCaretActionsMouseUpRoot.php
new file mode 100644
index 0000000..b7553ea
--- /dev/null
+++ b/src/Types/DocAnnotationsList200ResponseAnnotationsItemCaretActionsMouseUpRoot.php
@@ -0,0 +1,59 @@
+ $type
+ */
+ #[JsonProperty('type')]
+ public string $type;
+
+ /**
+ * @var string $subtype
+ */
+ #[JsonProperty('subtype')]
+ public string $subtype;
+
+ /**
+ * @var ?string $script
+ */
+ #[JsonProperty('script')]
+ public ?string $script;
+
+ /**
+ * @var array $next
+ */
+ #[JsonProperty('next'), ArrayType(['mixed'])]
+ public array $next;
+
+ /**
+ * @param array{
+ * type: value-of,
+ * subtype: string,
+ * next: array,
+ * script?: ?string,
+ * } $values
+ */
+ public function __construct(
+ array $values,
+ ) {
+ $this->type = $values['type'];
+ $this->subtype = $values['subtype'];
+ $this->script = $values['script'] ?? null;
+ $this->next = $values['next'];
+ }
+
+ /**
+ * @return string
+ */
+ public function __toString(): string
+ {
+ return $this->toJson();
+ }
+}
diff --git a/src/Types/DocAnnotationsList200ResponseAnnotationsItemCaretActionsMouseUpRootType.php b/src/Types/DocAnnotationsList200ResponseAnnotationsItemCaretActionsMouseUpRootType.php
new file mode 100644
index 0000000..4a4c212
--- /dev/null
+++ b/src/Types/DocAnnotationsList200ResponseAnnotationsItemCaretActionsMouseUpRootType.php
@@ -0,0 +1,26 @@
+> $warnings
+ */
+ #[JsonProperty('warnings'), ArrayType(['string'])]
+ public array $warnings;
+
+ /**
+ * @param array{
+ * incomplete: bool,
+ * warningFlags: int,
+ * warnings: array>,
+ * root?: ?DocAnnotationsList200ResponseAnnotationsItemCaretActionsPageCloseRoot,
+ * } $values
+ */
+ public function __construct(
+ array $values,
+ ) {
+ $this->root = $values['root'] ?? null;
+ $this->incomplete = $values['incomplete'];
+ $this->warningFlags = $values['warningFlags'];
+ $this->warnings = $values['warnings'];
+ }
+
+ /**
+ * @return string
+ */
+ public function __toString(): string
+ {
+ return $this->toJson();
+ }
+}
diff --git a/src/Types/DocAnnotationsList200ResponseAnnotationsItemCaretActionsPageCloseRoot.php b/src/Types/DocAnnotationsList200ResponseAnnotationsItemCaretActionsPageCloseRoot.php
new file mode 100644
index 0000000..494d83d
--- /dev/null
+++ b/src/Types/DocAnnotationsList200ResponseAnnotationsItemCaretActionsPageCloseRoot.php
@@ -0,0 +1,59 @@
+ $type
+ */
+ #[JsonProperty('type')]
+ public string $type;
+
+ /**
+ * @var string $subtype
+ */
+ #[JsonProperty('subtype')]
+ public string $subtype;
+
+ /**
+ * @var ?string $script
+ */
+ #[JsonProperty('script')]
+ public ?string $script;
+
+ /**
+ * @var array $next
+ */
+ #[JsonProperty('next'), ArrayType(['mixed'])]
+ public array $next;
+
+ /**
+ * @param array{
+ * type: value-of,
+ * subtype: string,
+ * next: array,
+ * script?: ?string,
+ * } $values
+ */
+ public function __construct(
+ array $values,
+ ) {
+ $this->type = $values['type'];
+ $this->subtype = $values['subtype'];
+ $this->script = $values['script'] ?? null;
+ $this->next = $values['next'];
+ }
+
+ /**
+ * @return string
+ */
+ public function __toString(): string
+ {
+ return $this->toJson();
+ }
+}
diff --git a/src/Types/DocAnnotationsList200ResponseAnnotationsItemCaretActionsPageCloseRootType.php b/src/Types/DocAnnotationsList200ResponseAnnotationsItemCaretActionsPageCloseRootType.php
new file mode 100644
index 0000000..a6fdcb6
--- /dev/null
+++ b/src/Types/DocAnnotationsList200ResponseAnnotationsItemCaretActionsPageCloseRootType.php
@@ -0,0 +1,26 @@
+> $warnings
+ */
+ #[JsonProperty('warnings'), ArrayType(['string'])]
+ public array $warnings;
+
+ /**
+ * @param array{
+ * incomplete: bool,
+ * warningFlags: int,
+ * warnings: array>,
+ * root?: ?DocAnnotationsList200ResponseAnnotationsItemCaretActionsPageInvisibleRoot,
+ * } $values
+ */
+ public function __construct(
+ array $values,
+ ) {
+ $this->root = $values['root'] ?? null;
+ $this->incomplete = $values['incomplete'];
+ $this->warningFlags = $values['warningFlags'];
+ $this->warnings = $values['warnings'];
+ }
+
+ /**
+ * @return string
+ */
+ public function __toString(): string
+ {
+ return $this->toJson();
+ }
+}
diff --git a/src/Types/DocAnnotationsList200ResponseAnnotationsItemCaretActionsPageInvisibleRoot.php b/src/Types/DocAnnotationsList200ResponseAnnotationsItemCaretActionsPageInvisibleRoot.php
new file mode 100644
index 0000000..184a4ed
--- /dev/null
+++ b/src/Types/DocAnnotationsList200ResponseAnnotationsItemCaretActionsPageInvisibleRoot.php
@@ -0,0 +1,59 @@
+ $type
+ */
+ #[JsonProperty('type')]
+ public string $type;
+
+ /**
+ * @var string $subtype
+ */
+ #[JsonProperty('subtype')]
+ public string $subtype;
+
+ /**
+ * @var ?string $script
+ */
+ #[JsonProperty('script')]
+ public ?string $script;
+
+ /**
+ * @var array $next
+ */
+ #[JsonProperty('next'), ArrayType(['mixed'])]
+ public array $next;
+
+ /**
+ * @param array{
+ * type: value-of,
+ * subtype: string,
+ * next: array,
+ * script?: ?string,
+ * } $values
+ */
+ public function __construct(
+ array $values,
+ ) {
+ $this->type = $values['type'];
+ $this->subtype = $values['subtype'];
+ $this->script = $values['script'] ?? null;
+ $this->next = $values['next'];
+ }
+
+ /**
+ * @return string
+ */
+ public function __toString(): string
+ {
+ return $this->toJson();
+ }
+}
diff --git a/src/Types/DocAnnotationsList200ResponseAnnotationsItemCaretActionsPageInvisibleRootType.php b/src/Types/DocAnnotationsList200ResponseAnnotationsItemCaretActionsPageInvisibleRootType.php
new file mode 100644
index 0000000..c411fe1
--- /dev/null
+++ b/src/Types/DocAnnotationsList200ResponseAnnotationsItemCaretActionsPageInvisibleRootType.php
@@ -0,0 +1,26 @@
+> $warnings
+ */
+ #[JsonProperty('warnings'), ArrayType(['string'])]
+ public array $warnings;
+
+ /**
+ * @param array{
+ * incomplete: bool,
+ * warningFlags: int,
+ * warnings: array>,
+ * root?: ?DocAnnotationsList200ResponseAnnotationsItemCaretActionsPageOpenRoot,
+ * } $values
+ */
+ public function __construct(
+ array $values,
+ ) {
+ $this->root = $values['root'] ?? null;
+ $this->incomplete = $values['incomplete'];
+ $this->warningFlags = $values['warningFlags'];
+ $this->warnings = $values['warnings'];
+ }
+
+ /**
+ * @return string
+ */
+ public function __toString(): string
+ {
+ return $this->toJson();
+ }
+}
diff --git a/src/Types/DocAnnotationsList200ResponseAnnotationsItemCaretActionsPageOpenRoot.php b/src/Types/DocAnnotationsList200ResponseAnnotationsItemCaretActionsPageOpenRoot.php
new file mode 100644
index 0000000..31b0c02
--- /dev/null
+++ b/src/Types/DocAnnotationsList200ResponseAnnotationsItemCaretActionsPageOpenRoot.php
@@ -0,0 +1,59 @@
+ $type
+ */
+ #[JsonProperty('type')]
+ public string $type;
+
+ /**
+ * @var string $subtype
+ */
+ #[JsonProperty('subtype')]
+ public string $subtype;
+
+ /**
+ * @var ?string $script
+ */
+ #[JsonProperty('script')]
+ public ?string $script;
+
+ /**
+ * @var array $next
+ */
+ #[JsonProperty('next'), ArrayType(['mixed'])]
+ public array $next;
+
+ /**
+ * @param array{
+ * type: value-of,
+ * subtype: string,
+ * next: array,
+ * script?: ?string,
+ * } $values
+ */
+ public function __construct(
+ array $values,
+ ) {
+ $this->type = $values['type'];
+ $this->subtype = $values['subtype'];
+ $this->script = $values['script'] ?? null;
+ $this->next = $values['next'];
+ }
+
+ /**
+ * @return string
+ */
+ public function __toString(): string
+ {
+ return $this->toJson();
+ }
+}
diff --git a/src/Types/DocAnnotationsList200ResponseAnnotationsItemCaretActionsPageOpenRootType.php b/src/Types/DocAnnotationsList200ResponseAnnotationsItemCaretActionsPageOpenRootType.php
new file mode 100644
index 0000000..eaaa259
--- /dev/null
+++ b/src/Types/DocAnnotationsList200ResponseAnnotationsItemCaretActionsPageOpenRootType.php
@@ -0,0 +1,26 @@
+> $warnings
+ */
+ #[JsonProperty('warnings'), ArrayType(['string'])]
+ public array $warnings;
+
+ /**
+ * @param array{
+ * incomplete: bool,
+ * warningFlags: int,
+ * warnings: array