rerout / sdk
Official PHP SDK for the Rerout branded-link API.
Requires
- php: ^8.2
- ext-hash: *
- ext-json: *
- guzzlehttp/guzzle: ^7.5
- psr/http-message: ^1.0 || ^2.0
Requires (Dev)
- friendsofphp/php-cs-fixer: ^3.0
- phpstan/phpstan: ^1.10
- phpunit/phpunit: ^11.0
This package is not auto-updated.
Last update: 2026-05-22 19:44:45 UTC
README
Official PHP SDK for the Rerout API.
Branded link infrastructure on Cloudflare — create short links, render QR codes, read analytics, and verify webhook signatures.
Install
composer require rerout/sdk
Requires PHP 8.2+ and the json and hash extensions. HTTP transport is
Guzzle 7.
Usage
use Rerout\Rerout; use Rerout\Models\CreateLinkInput; $rerout = new Rerout(getenv('REROUT_API_KEY')); $link = $rerout->links()->create(new CreateLinkInput( targetUrl: 'https://example.com/q4-sale', domainHostname: 'go.brand.com', code: 'q4', )); echo $link->shortUrl; // https://go.brand.com/q4 $stats = $rerout->project()->stats(7); echo "Last 7 days: {$stats->totalClicks} clicks, {$stats->qrScans} QR scans";
API
Construction
$rerout = new Rerout('rrk_…', [ 'base_url' => 'https://api.rerout.co', // optional, default shown 'timeout' => 30, // optional, seconds 'client' => $guzzleClient, // optional — inject your own ClientInterface 'default_headers' => [ // optional — added to every request 'User-Agent' => 'my-app/1.0', ], ]);
A blank or missing API key throws a ReroutException with code
missing_api_key. The base_url has trailing slashes trimmed.
Links
use Rerout\Models\CreateLinkInput; use Rerout\Models\UpdateLinkInput; $rerout->links()->create(new CreateLinkInput( targetUrl: 'https://example.com', domainHostname: 'go.brand.com', // optional code: 'promo', // optional expiresAt: 1893456000, // optional, unix seconds seoTitle: 'Big Sale', // optional SEO overrides )); $result = $rerout->links()->list(cursor: null, limit: 50); foreach ($result->links as $link) { echo $link->shortUrl, PHP_EOL; } // $result->nextCursor — pass back as `cursor` for the next page $link = $rerout->links()->get('promo'); $link = $rerout->links()->update('promo', new UpdateLinkInput( isActive: false, )); $deleted = $rerout->links()->delete('promo'); // bool $stats = $rerout->links()->stats('promo', days: 30);
UpdateLinkInput distinguishes three states per field:
- Leave alone — omit the argument (the default). The field is not sent.
- Set a value — pass a concrete value.
- Clear server-side — pass
UpdateLinkInput::CLEAR, which serialises as explicitnull.
new UpdateLinkInput( targetUrl: 'https://example.com/v2', // set expiresAt: UpdateLinkInput::CLEAR, // null it on the server // seoTitle omitted — left untouched );
Constructing an empty UpdateLinkInput and calling update() throws a
ReroutException (code bad_request) client-side — the request never leaves
your process.
Project
$stats = $rerout->project()->stats(days: 30); echo $stats->totalClicks, ' ', $stats->qrScans; $me = $rerout->project()->me(); // array{id: string, name: string, slug: string}
QR codes
use Rerout\Models\QrOptions; // Pure URL builder — no network call. $url = $rerout->qr()->url('promo', new QrOptions( size: 12, margin: 2, ecc: 'H', domain: 'go.brand.com', refresh: true, // true → `refresh=1`; any string is forwarded verbatim )); // Fetch the rendered SVG (sends the bearer token). $svg = $rerout->qr()->svg('promo', new QrOptions(size: 8));
Webhook signature verification
Rerout signs every webhook delivery with an X-Rerout-Signature header in the
form t=<unix>,v1=<hex_hmac_sha256>. Verify it before trusting the payload:
use Rerout\Webhooks\SignatureVerifier; $ok = SignatureVerifier::verify( rawBody: file_get_contents('php://input'), signatureHeader: $_SERVER['HTTP_X_REROUT_SIGNATURE'] ?? '', secret: getenv('REROUT_WEBHOOK_SECRET'), ); if (!$ok) { http_response_code(401); exit; }
The HMAC is SHA-256 over "<timestamp>.<raw_body>" and is compared in constant
time. The default timestamp tolerance is 300 seconds — pass
toleranceSeconds: 0 to disable the staleness check, or a custom value to
widen it. A $clock callable can be injected for testing.
Error handling
Every API call throws Rerout\Exceptions\ReroutException on failure:
use Rerout\Exceptions\ReroutException; try { $rerout->links()->create(new CreateLinkInput(targetUrl: 'http://insecure')); } catch (ReroutException $e) { echo $e->code(); // 'bad_target_url' echo $e->status(); // 400 echo $e->getMessage(); // 'target_url must use https.' echo $e->path ?? ''; // the API path that failed if ($e->isRateLimited()) { /* back off */ } if ($e->isServerError()) { /* retry later */ } }
When the server returns a JSON error body, code and message are taken
verbatim. When it does not, a synthetic code is used:
| Condition | Code |
|---|---|
| HTTP 401 | unauthorized |
| HTTP 403 | forbidden |
| HTTP 404 | not_found |
| HTTP 429 | rate_limited |
| HTTP 5xx | server_error |
| other 4xx | client_error |
| connection failure | network_error |
| timeout | timeout |
| 2xx non-JSON body | unexpected_response |
Local development
composer install composer test # PHPUnit composer analyse # PHPStan, level 9 composer format # php-cs-fixer
License
MIT — see LICENSE.