newfoundcodes / okatana-php
A typed PHP SDK for the Okatana External API v1.
Requires
- php: ^8.2
- ext-json: *
- guzzlehttp/guzzle: ^7.9
- psr/http-client: ^1.0
- psr/http-factory: ^1.0
- psr/http-message: ^1.1 || ^2.0
- psr/log: ^3.0
Requires (Dev)
- laravel/pint: ^1.30
- phpstan/phpstan: ^2.1
- phpunit/phpunit: ^11.5
- symfony/yaml: ^7.2
Suggests
None
Provides
None
Conflicts
None
Replaces
None
This package is auto-updated.
Last update: 2026-08-31 15:08:12 UTC
README
Okatana PHP SDK
PHP 8.2+ SDK for the Okatana External API v1. Provides request objects, response helpers, pagination support, typed models, explicit exception mapping, safe read retries, PSR-18 transport replacement, PHPUnit tests, and Material for MkDocs documentation.
API URL configuration
https://okatana.newfoundcodes.com is documentation. This SDK does not use it as a default API host.
Every client must receive the URL of the user's own Okatana deployment:
use Newfoundcodes\Okatana\OkatanaClient; $client = OkatanaClient::create( baseUrl: 'https://project-management.example.com', apiKey: 'oka_public.secret', );
The client accepts either a deployment origin (https://project-management.example.com) or a full v1 base (https://project-management.example.com/api/v1). Origin URLs append /api/v1 automatically.
Quick Start
Install the package via Composer:
composer require newfoundcodes/okatana-php
Then, initialize the client and make your first request:
use Newfoundcodes\Okatana\OkatanaClient; $client = OkatanaClient::create( baseUrl: getenv('OKATANA_URL'), apiKey: getenv('OKATANA_TOKEN'), ); $organization = $client->organizations()->get(getenv('OKATANA_ORG')); print_r($organization->data());
Typed request example
use Newfoundcodes\Okatana\Request\CreateTicketRequest; use Newfoundcodes\Okatana\TicketPriority; $response = $client->tickets()->create( project: $projectId, request: new CreateTicketRequest( title: 'Validate production deployment', boardSlug: 'open', descriptionHtml: '<p>Run deployment checks.</p>', priority: TicketPriority::High, assigneeIds: [$operatorId], labelIds: [$releaseLabelId], ), ); $ticket = $response->data();
Fluent builders preserve explicit null values in PATCH operations:
use Newfoundcodes\Okatana\Request\UpdateTicketRequest; $client->tickets()->update( $ticketId, UpdateTicketRequest::create() ->descriptionHtml(null) // send JSON null ->archived(false), );
API services
organizations()— read organization.projects()— list/create/read/update/delete projects; members; labels; tags; analytics.boards()— list/create/update/reorder/delete boards.tickets()— list/create/reorder/read/update/delete/move/comment tickets.documents()— list/create/read/update/delete/comment documents.notifications()— send organization notifications.
Methods return ApiResponse (including 204 operations) to expose HTTP status and headers.
Response helpers
$response->statusCode; $response->data(); // top-level data, or the raw decoded body when no data envelope exists $response->collection(); // simple collection or nested paginator items $response->pagination(); // Laravel paginator metadata $response->header('Retry-After'); $response->requestId();
Typed component models provide optional wrappers:
use Newfoundcodes\Okatana\Model\Project; use Newfoundcodes\Okatana\Model\Ticket; $project = Project::fromResponse($client->projects()->get($projectId)); $ticket = Ticket::fromResponse($client->tickets()->get($ticketId));
Models retain the source object in $model->raw to preserve unknown fields.
Errors
Common Okatana statuses map to specific exceptions:
use Newfoundcodes\Okatana\Exception\AuthenticationException; use Newfoundcodes\Okatana\Exception\AuthorizationException; use Newfoundcodes\Okatana\Exception\NotFoundException; use Newfoundcodes\Okatana\Exception\RateLimitException; use Newfoundcodes\Okatana\Exception\ValidationException; try { $client->tickets()->create($projectId, ['title' => 'Deploy']); } catch (ValidationException $e) { print_r($e->errors()); } catch (RateLimitException $e) { echo $e->retryAfterSeconds(); }
Retry behavior
The default policy retries safe read methods (GET, HEAD) on transport failures and transient statuses. Write requests are not retried automatically as the Okatana API lacks idempotency keys.
use Newfoundcodes\Okatana\Configuration; use Newfoundcodes\Okatana\Http\RetryPolicy; use Newfoundcodes\Okatana\OkatanaClient; $client = new OkatanaClient(new Configuration( baseUrl: $_ENV['OKATANA_URL'], apiKey: $_ENV['OKATANA_TOKEN'], retryPolicy: RetryPolicy::disabled(), ));
Custom PSR-18 HTTP client
$config = new Configuration($_ENV['OKATANA_URL'], $_ENV['OKATANA_TOKEN']); $client = new OkatanaClient($config, httpClient: $myPsr18Client);
Configure transport-specific timeouts on the custom client directly.
Development
composer install
composer test
composer analyse
Build the documentation:
python -m pip install -r requirements-docs.txt mkdocs serve
See docs/ for the complete manual and examples/ for executable scenarios.