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

# PHP

> Use the official Onlyform SDK from Packagist.

`onlyform/onlyform-php` is the official PHP 8.1+ package. It has no third-party runtime dependencies and includes a cURL transport.
The current release targets API contract `2026-08-01`.

## Install

```bash theme={null}
composer require onlyform/onlyform-php
```

## Configure the client

```php theme={null}
use Onlyform\Client;

$onlyform = new Client(
    apiKey: $_ENV['ONLYFORM_API_KEY'],
);
```

The client uses `https://api.onlyform.com` by default. Use the optional `baseUrl` constructor argument only for private development environments.

## Create a complete form

The create call accepts blocks, settings, and theme tokens inside `definition`.

```php theme={null}
$created = $onlyform->forms()->create(
    name: 'Product feedback',
    workspaceId: '507f1f77bcf86cd799439012',
    type: 'MULTI',
    definition: [
        'blocks' => [[
            'id' => 'rating',
            'type' => 'rating',
            'label' => 'How would you rate the product?',
            'description' => '',
            'required' => true,
            'properties' => ['max' => 5, 'icon' => 'star'],
        ]],
        'settings' => ['progressBar' => true],
        'theme' => [
            'font' => 'Inter',
            'colors' => ['primary' => '#3969D9'],
        ],
    ],
);

echo $created['id'];
```

See [Form definitions](/guides/form-definitions), [Block types](/schema/block-types), and [Theme tokens](/schema/theme) for the complete accepted schema.

## Add and remove blocks

Versioned form and block operations return a `VersionedResponse`. Pass its ETag back with `ifMatch` when editing blocks.

```php theme={null}
$current = $onlyform->forms()->retrieve('form_id');

$added = $onlyform->forms()->blocks()->create(
    formId: 'form_id',
    block: [
        'id' => 'email',
        'type' => 'email',
        'label' => 'Email address',
        'description' => 'Where should we reply?',
        'required' => true,
        'properties' => [],
    ],
    ifMatch: $current->etag,
);

$onlyform->forms()->blocks()->delete(
    formId: 'form_id',
    blockId: 'email',
    ifMatch: $added->etag,
);
```

Use `replaceAll($formId, $blocks, $ifMatch)` to atomically replace the complete ordered block collection.

## Update a form

```php theme={null}
$current = $onlyform->forms()->retrieve('form_id');

$updated = $onlyform->forms()->update(
    formId: 'form_id',
    changes: [
        'name' => 'Customer feedback',
        'definition' => [
            'settings' => ['progressBar' => false],
            'theme' => ['font' => 'Georgia', 'colors' => ['primary' => '#3969D9']],
        ],
    ],
    ifMatch: $current->etag,
);
```

## Cursor pagination

```php theme={null}
$cursor = null;

do {
    $page = $onlyform->forms()->list(limit: 100, cursor: $cursor);

    foreach ($page['items'] as $form) {
        echo $form['id'] . PHP_EOL;
    }

    $cursor = $page['page']['next_cursor'];
} while ($cursor !== null);
```

## Other resources

```php theme={null}
$workspaces = $onlyform->workspaces()->list(limit: 50);
$submissions = $onlyform->forms()->submissions()->list('form_id', limit: 100);
$metrics = $onlyform->forms()->analytics()->metrics('form_id', period: '30d');
$webhooks = $onlyform->webhooks()->list();
```

## Handle errors

```php theme={null}
use Onlyform\Exception\ApiException;
use Onlyform\Exception\TransportException;

try {
    $onlyform->forms()->retrieve('form_id');
} catch (ApiException $error) {
    error_log(sprintf(
        '%d %s request=%s',
        $error->status,
        $error->errorCode,
        $error->requestId ?? 'unknown',
    ));
} catch (TransportException $error) {
    error_log($error->getMessage());
}
```
