Configure the client
API key setup, environment inference and transport options for the SUQO PHP SDK.
Suqo\SuqoClient is the whole entry point: construct it, then reach a resource through one of its readonly properties. It is final and exposes resources and nothing else — no request method, no header hook, no mutable state.
Getting a key
Generate an API key in the API Integration section of your seller dashboard. Sandbox keys start su_test_key_, live keys start su_key_.
The SDK reads $SUQO_API_KEY when you pass nothing:
use Suqo\SuqoClient;
$suqo = new SuqoClient(); // from $SUQO_API_KEY
$suqo = new SuqoClient(apiKey: 'su_test_key_…'); // or explicitlyThe variable is read through getenv(), then $_ENV, then $_SERVER. An empty or absent key raises SuqoConfigError.
There's no separate environment flag
The key's prefix decides which host the SDK talks to: su_test_key_ → https://test-be.suqo.ai, su_key_ → https://be.suqo.ai. Swapping environments means swapping the key, and nothing else.
You can pass environment — but it is a check, never an override:
$suqo = new SuqoClient(apiKey: $key, environment: 'sandbox');If it disagrees with the prefix, construction raises SuqoConfigError rather than quietly sending a live key to the sandbox or the reverse.
The resolved configuration is readable:
echo $suqo->config->environment->value; // "sandbox"
echo $suqo->config->baseUrl; // "https://test-be.suqo.ai"Constructor options
public function __construct(
?string $apiKey = null,
Environment|string|null $environment = null,
?float $timeout = null,
?int $maxRetries = null,
LogLevel|string|null $logLevel = null,
?HttpClientInterface $httpClient = null,
)| Parameter | Default | Notes |
|---|---|---|
apiKey | $SUQO_API_KEY | Must start su_test_key_ or su_key_. |
environment | inferred | A check against the prefix, never an override. |
timeout | 30.0 | Seconds, per attempt — connect and total. Must be greater than zero. |
maxRetries | 2 | Retries after the first attempt, so 2 means three attempts. Must be zero or greater. |
logLevel | $SUQO_LOG, else warn | debug, info, warn, error, off. |
httpClient | new CurlHttpClient() | See Inject an HTTP client. |
$suqo = new SuqoClient(
apiKey: 'su_test_key_…',
timeout: 30.0,
maxRetries: 2,
logLevel: 'warn',
);Errors raised before any request is made
Construction raises Suqo\Exception\SuqoConfigError — a missing or malformed key, an environment that disagrees with the prefix, an unparseable environment or logLevel, a timeout of zero or less, a negative maxRetries.
use Suqo\Exception\SuqoConfigError;
try {
$suqo = new SuqoClient(apiKey: $key);
} catch (SuqoConfigError $e) {
fwrite(STDERR, $e->getMessage() . PHP_EOL);
exit(1);
}SuqoConfigError extends SuqoError, so a single catch (SuqoError) covers startup and runtime alike.
One asymmetry worth knowing: an unrecognised $SUQO_LOG value falls back to the default rather than failing construction — the environment is not the caller's call site. An unrecognised logLevel argument does throw.
Validating a key without building a client
Config::resolve() takes the same parameters and raises the same error:
use Suqo\Config;
$config = Config::resolve(apiKey: $candidate); // throws SuqoConfigError if badLogging
Diagnostics go to STDERR, so they never contaminate STDOUT in a CLI program. There is no logger injection point: set logLevel (or $SUQO_LOG) and the SDK builds its own.
$suqo = new SuqoClient(logLevel: 'debug');
// [suqo] debug request {"method":"GET","url":"https://…/api/v1/products/","request_id":"…"}
// [suqo] debug response {"status":200,"request_id":"…","elapsed_ms":184}The SDK logs request and response at debug, and retry at warn with the attempt number and delay. The API key is never a logged value.
Retries and timeouts
GET requests are retried up to maxRetries times — three attempts by default — on a network failure, a timeout, a 429 or a 5xx. Backoff is full jitter: a uniform draw across [0, cap) with cap = min(8000, 500 × 2 ** attempt) milliseconds. A server-supplied Retry-After wins, is not jittered, and is capped at 60 seconds.
Writes are not retried, pending idempotency keys — see Not yet implemented.
Every attempt carries a fresh X-Request-Id, so a retry has a new id. Quote the one on the error in a support ticket:
catch (\Suqo\Exception\SuqoError $e) {
error_log("suqo {$e->status} req={$e->requestId}: {$e->getMessage()}");
}The key is sent on every request
Authorization: Bearer <key> goes on every request the SDK makes. Two consequences worth reading before you change the transport: redirects are never followed, and TLS verification is yours to keep. Both are covered in Inject an HTTP client.