> ## Documentation Index
> Fetch the complete documentation index at: https://developers.octaviatech.app/llms.txt
> Use this file to discover all available pages before exploring further.

# PHP SDK

> PHP client for the Octavia AI CMS API.

PHP 8.0+, PSR-18 compatible, distributed as a Composer package. Can be pointed at any
PSR-18 HTTP client.

## Install

```bash theme={null}
composer require octavia/cms-sdk
```

## Initialize

```php theme={null}
<?php

use Octavia\Cms\CMS;

$cms = CMS::init(getenv('OCTAVIA_API_KEY'), [
    'timeoutMs'    => 30_000,
    'throwOnError' => false,
]);
```

The key is sent as the `x-api-key` header on every request. There is no header option,
by design.

## Make a request

Read operations that take filters take a single associative array:

```php theme={null}
$res = $cms->article->getAll(['page' => 1, 'limit' => 10, 'categoryId' => '...']);

if ($res->ok) {
    print_r($res->data);
    print_r($res->meta);
} else {
    echo $res->error->getMessage();
}
```

Single-resource and search operations use the same shape:

```php theme={null}
$one     = $cms->article->getById('6810f2c3a1b2c3d4e5f60718');
$hits    = $cms->article->search(['query' => 'typescript', 'limit' => 5]);
$bySlug  = $cms->article->getBySlug('hello-world');
```

## Write operations

```php theme={null}
$created = $cms->article->create([
    'title'      => 'Hello world',
    'body'       => '...',
    'categoryId' => '6810f2c3a1b2c3d4e5f60712',
    'authorId'   => '6810f2c3a1b2c3d4e5f60719',
]);

$updated = $cms->article->update($created->data->id, ['title' => 'Updated']);
$cms->article->delete($created->data->id);
```

## Error handling

Errors are return values, not exceptions, unless you opt in:

```php theme={null}
$res = $cms->article->getById('does-not-exist');

if (!$res->ok) {
    echo $res->error->getCode();     // 404
    echo $res->error->getMessage();  // human readable
    print_r($res->error->payload);   // full API body
}
```

To throw an `ApiError` instead:

```php theme={null}
use Octavia\Cms\ApiError;

$cms = CMS::init($key, ['throwOnError' => true]);

try {
    $cms->article->getById('does-not-exist');
} catch (ApiError $err) {
    echo $err->status;
    print_r($err->payload);
}
```

## Timeouts

```php theme={null}
$cms = CMS::init($key, ['timeoutMs' => 5000]);
```

<Warning>
  `timeoutMs` defaults to `0`, which means **no timeout** — a request will hang until
  the server or PHP's `max_execution_time` stops it. Set it explicitly in production.
</Warning>

## Escaping the resource wrapper

`$cms->raw` is the underlying HTTP client, for calling endpoints the resources do not
cover. It sends only `x-api-key`; the tenant and service state are derived by the
gateway, so there is no header to set by hand:

```php theme={null}
$res = $cms->raw->request('GET', '/articles/advancedSearch');
```

## Resources

`article`, `author`, `category`, `subcategory`, `form`, `formSubmission`, `language`,
`tag`, `report`, `ai`, `aiConversation`, `raw`.

<Card title="All operations" icon="list" href="/api-reference/ai-cms/articles/get-all" arrow="true">
  Browse the API reference for the full endpoint list.
</Card>
