Shared base for building PHP clients for Keboola services (Storage, Manage, Vault, Git Service, Sandboxes, Sync Actions, Job Queue, …). It is not a general-purpose HTTP client — it encodes Keboola platform conventions: the Keboola authentication headers, retry behavior, JSON handling, and error normalization that every Keboola service client needs.
Used by keboola/vault-api-client, keboola/sandboxes-service-api-client,
keboola/git-service-api-client, keboola/sync-actions-client,
keboola/azure-api-client, and new Keboola service clients.
composer require keboola/php-api-client-baseApiClient— Guzzle wrapper with per-request auth, retry, logging, and response-to-model mapping. Constructed asnew ApiClient($baseUrl, $authenticator, $options, errorMessageResolver: ..., retryableStatusCodes: [...]). The authenticator is required; passnew NoAuthAuthenticator()for unauthenticated clients.errorMessageResolveraccepts a?ErrorMessageResolverInterfaceinstance; whennull, the shippedDefaultErrorMessageResolver(which extractserrorormessagefrom JSON bodies) is used automatically.retryableStatusCodesareApiClientconstructor arguments supplied by the service facade (they describe the service's API contract, not caller preferences).ApiClientOptions— retries, timeouts, logger (no auth, no error resolver — the authenticator is a first-classApiClientconstructor argument; the error resolver and retryable codes are alsoApiClientconstructor arguments).Auth\RequestAuthenticatorInterface+ ready authenticators for the Keboola auth schemes:StorageApiTokenAuthenticator(X-StorageApi-Token),ManageApiTokenAuthenticator(X-KBC-ManageApiToken),KeboolaServiceAccountAuthenticator(projected SA token →X-Kubernetes-Authorization),NoAuthAuthenticator(explicit no-op for unauthenticated calls).ErrorMessageResolverInterface,DefaultErrorMessageResolver,RetryDecider,Json,ResponseModelInterface,Exception\ClientException.
Compose an ApiClient inside your service facade and map responses to models:
use GuzzleHttp\Psr7\Request;
use Keboola\ApiClientBase\ApiClient;
use Keboola\ApiClientBase\ApiClientOptions;
use Keboola\ApiClientBase\Auth\StorageApiTokenAuthenticator;
use Keboola\ApiClientBase\ErrorMessageResolverInterface;
use Keboola\ApiClientBase\Json;
use Keboola\ApiClientBase\ResponseModelInterface;
final class WidgetModel implements ResponseModelInterface
{
public function __construct(public readonly string $id) {}
public static function fromResponseData(array $data): static
{
\assert(is_string($data['id']));
return new self($data['id']);
}
}
final class MyServiceErrorResolver implements ErrorMessageResolverInterface
{
public function __invoke(string $responseBody, int $statusCode): ?string
{
/** @var array{error?: string} $data */
$data = json_decode($responseBody, true) ?? [];
return isset($data['error']) && $data['error'] !== '' ? $data['error'] : null;
}
}
final class MyServiceClient
{
private ApiClient $apiClient;
public function __construct(
string $baseUrl,
StorageApiTokenAuthenticator $authenticator,
?ApiClientOptions $options = null,
) {
$this->apiClient = new ApiClient(
$baseUrl,
$authenticator,
$options,
errorMessageResolver: new MyServiceErrorResolver(),
retryableStatusCodes: [429],
);
}
public function createWidget(string $name): WidgetModel
{
return $this->apiClient->sendRequestAndMapResponse(
new Request('POST', 'widgets', ['Content-Type' => 'application/json'], Json::encodeArray(['name' => $name])),
WidgetModel::class,
);
}
}
$client = new MyServiceClient(
'https://my-service.keboola.com',
new StorageApiTokenAuthenticator($storageApiToken),
new ApiClientOptions(backoffMaxTries: 3),
);Pick the authenticator matching the service's scheme, or implement
RequestAuthenticatorInterface for a service-specific scheme (e.g. azure's
OAuth).
KeboolaServiceAccountAuthenticator re-reads the projected SA token on every
request, so kubelet-rotated tokens are picked up automatically. The read is
hardened against the rotation race (read-first, clear PHP's stat cache and
re-check on failure, then retry empty/failed reads with bounded exponential
backoff — ~1.24 s over 5 retries by default, tunable via the maxReadAttempts
and retryBaseDelayMicroseconds constructor arguments).
MIT