Skip to documentation content

PHP SDK

A Composer package built around PSR standards.

In this guide
  1. 1Understand the package contract
  2. 2Run or resume jobs
  3. 3Handle native errors
  4. 4Verify webhooks
In this guide
  1. 1Understand the package contract
  2. 2Run or resume jobs
  3. 3Handle native errors
  4. 4Verify webhooks

Package contract

The planned Composer package is nordicdevhouse/sdk under the NordicDevhouse\Sdk namespace. It follows PSR-4, accepts PSR-18 transports, and exposes immutable value objects.

DistributionNamespace or importRuntime
nordicdevhouse/sdkNordicDevhouse\SdkPHP 8.2+ and PSR-compatible runtimes

Create and reuse the client

The client is framework-neutral. A factory discovers installed PSR implementations, while explicit constructor injection keeps transport and clock behaviour testable.

PHP
use NordicDevhouse\Sdk\Client;
use NordicDevhouse\Sdk\ScrapeRequest;

$client = Client::fromApiKey(
    $_ENV['NORDIC_DEVHOUSE_API_KEY'],
    maxRetries: 3,
);

$result = $client->scrape()->run(new ScrapeRequest(
    url: 'https://example.com/products',
    formats: ['json'],
    idempotencyKey: 'catalog-2026-08-11',
));

echo $result->jobId;

Control a durable job

Start and wait model durable asynchronous server work without requiring promises in the public interface. Queue workers persist the returned job ID between calls.

PHP
$job = $client->scrape()->start(new ScrapeRequest(
    url: 'https://example.com/products',
    formats: ['json'],
));

$result = $client->jobs()->wait(
    id: $job->id,
    timeout: new DateInterval('PT10M'),
);

Public models

Names follow the language conventions, but every SDK preserves the same fields and job-state semantics.

ConceptContract
Client optionsAPI key, base URL, request timeout, retry budget, and optional test transport
Scrape requestURL, output formats, webhook URL, metadata, and idempotency key
JobStable ID, status, timestamps, progress, and warnings
Job resultJob ID, record count, typed data, metadata, and request ID
Webhook eventVerified event ID, type, timestamp, and typed data

Handle errors idiomatically

Typed exceptions implement a common NordicDevhouseException interface. Retryable exceptions expose Retry-After as a DateInterval and validation exceptions expose field-indexed issues.

PHP
try {
    return $client->scrape()->run($request);
} catch (RateLimitException $error) {
    $logger->warning('Rate limited', [
        'request_id' => $error->requestId,
        'retry_after' => $error->retryAfter,
    ]);
    throw $error;
} catch (ValidationException $error) {
    return validationResponse($error->issues);
} catch (NordicDevhouseException $error) {
    $logger->error('SDK failure', ['request_id' => $error->requestId]);
    throw $error;
}

Verify the raw webhook body

Verification uses the exact request bytes, validates HMAC-SHA256 and timestamp tolerance, and returns a typed event. Persist the verified event ID to prevent duplicate processing.

PHP
$event = $client->webhooks()->verify(
    payload: (string) $request->getBody(),
    signature: $request->getHeaderLine('Nordic-Signature'),
    secret: $_ENV['NORDIC_WEBHOOK_SECRET'],
);

if ($event->type === 'scrape.completed') {
    importResult($event->data->jobId);
}

Runtime and lifecycle rules

  1. 1
    Require PHP 8.2 and strict types in package source
  2. 2
    Follow PSR-4 and accept PSR-18, PSR-17, and PSR-3 adapters
  3. 3
    Keep response value objects immutable and fully annotated for static analysis
  4. 4
    Do not require Laravel or Symfony in the core package
  5. 5
    Publish framework providers as thin optional adapters
Was this page helpful?