Traqio.

SDKs

PHP and Laravel server SDK

Report sign-ups and purchases from a PHP or Laravel backend with traqio/traqio-php, and tie each one to the ad click it came from. Auto-discovered in Laravel.

traqio/traqio-php is the Traqio SDK for PHP and Laravel backends — the server half of Traqio's web SDK. The conversion that counts on a website — a sign-up, a subscription, a purchase — happens on your backend, where the user id is known. This package reports it with the trackID, the ad click id that @traqio/web kept in first-party storage since the landing page, and Traqio credits the campaign that brought the user — deterministically, by that click's id. No dependency beyond ext-curl; auto-discovered in Laravel.

Not on Packagist yet — early access

Package traqio/traqio-php · version 0.1.0The SDK packages are not on their public registries yet. To integrate today, ask for the build: hello@traqio.appHow far it is proven: Unit-tested, and run end-to-end against a running Traqio API: sign-up with a trackID → attributed.

Requirements#

  • PHP 8.1 or later, with ext-curl and ext-json. Laravel 12 or 13 for the auto-discovered integration; plain PHP, Symfony or any PSR-7 framework otherwise.
  • A server key for the product (tq_sk_…). Neither the app's SDK key nor a web key works here.
  • @traqio/web on the site, and ads pointing at tracking links with a website destination — that is how the trackID reaches your sign-up form.

Set up#

  1. Create a server key#

    In the console, open Integrations → SDK and create a server key. It is shown once: put it in .env, never in code or in a page. Traqio keeps only a fingerprint of it; a lost key is replaced by a new one, and the old one revoked.

    .env
    TRAQIO_SECRET_KEY=tq_sk_...
  2. Install the package#

    composer require traqio/traqio-php

    Until the package is on Packagist, add the folder we send you as a path repository:

    # the folder you received, committed inside the backend's repository
    composer config repositories.traqio path ./vendor-src/traqio-php
    composer require traqio/traqio-php:@dev
  3. Configure the client#

    Laravel: nothing to write — the service provider and the Traqio facade are discovered, and read TRAQIO_SECRET_KEY. To change the endpoint, the timeout or the retries, publish the config file:

    php artisan vendor:publish --tag=traqio-config

    Plain PHP: one client, built once.

    use Traqio\Traqio;
    
    $traqio = new Traqio(getenv('TRAQIO_SECRET_KEY'));
  4. Report the sign-up with its trackID#

    Call signup() once, where the account is created. @traqio/web writes the trackID in the traq_click cookie on your own domain, so the browser sends it with the sign-up request; TrackId::fromRequest() reads it.

    use Illuminate\Http\Request;
    use Traqio\Laravel\Facades\Traqio;
    use Traqio\TraqioException;
    use Traqio\TrackId;
    
    public function store(Request $request)
    {
        $user = User::create($request->validated());
    
        try {
            // Reads the raw Cookie header: Laravel's cookie decryption would drop traq_click.
            Traqio::signup((string) $user->id, TrackId::fromRequest($request));
        } catch (TraqioException $e) {
            report($e); // never fail the sign-up itself
        }
    
        return redirect('/welcome');
    }

    In Laravel, to keep the call out of the request, run it after the response is sent:

    dispatch(fn () => Traqio::signup((string) $user->id, $trackId))->afterResponse();

    If the backend is on another domain than the site, the cookie does not reach it: send Traqio.getTrackId() from the browser with the form instead. The sign-up is the moment the user is acquired; the last click before it, inside your attribution window, gets the credit.

  5. Track the conversions#

    Traqio::track((string) $user->id, 'purchase', [
        'value' => 49,
        'currency' => 'EUR',
        'transactionId' => $paymentIntent->id, // one payment, one row — whoever reports it
        'store' => 'stripe',
    ]);

    Conversions follow the user: they are credited to the campaign that acquired them.

What the response tells you#

$result = Traqio::signup((string) $user->id, $trackId);

$result->trackId;                 // "matched" | "unknown" | "expired" | "claimed" | "refused" | null
$result->acquired;                // true: the sign-up is recorded
$result->attribution->network;    // "meta", "tiktok"… or "organic"
$result->attribution->campaignId; // the campaign that brought the user
trackIdMeaning
matchedThe click is now this user's. A retry answers matched again.
unknownNo click of this product has that id — a typo, another product's link, or a value @traqio/web did not write.
expiredThe click is older than the product's attribution window.
claimedAnother user, or an app install, already owns that click. A click credits one identity.
refusedThe click failed an anti-fraud rule (it came through an anonymising network). It is counted, never credited.

What it does today, and what it does not#

Today
Tie a sign-up to its click, and say who was creditedYes — in the response
Record purchases with value, currency and payment id, deduplicatedYes
Leave your apps' installs, revenue and ROAS untouchedYes — never attached to an app install
Show web conversions in the console's reportsYes — per campaign, beside installs
Send web conversions to Meta or TikTokNot yet

Conversions sent here appear in Live events, marked as not matched to an install — expected for a website. A sign-up credited to a campaign is counted in the ROAS report as a web conversion of that campaign, beside its installs, and the response of each call gives you the attribution at once.

Errors and retries#

A network error, a timeout, a 429 or a 5xx is retried — twice by default, with the same body, so nothing is counted twice. A 4xx (wrong or revoked key, invalid payload) is not retried. Both end as a Traqio\TraqioException:

use Traqio\TraqioException;

try {
    Traqio::signup((string) $user->id, $trackId);
} catch (TraqioException $e) {
    // $e->status: 401 = wrong or revoked key, 400 = invalid payload, null = network
    Log::warning($e->getMessage(), ['status' => $e->status, 'body' => $e->body]);
}

Report after your own write

Call Traqio once the account or the payment is saved, and never let a Traqio error fail it.

API reference#

config/traqio.php
// config/traqio.php
return [
    'secret_key'  => env('TRAQIO_SECRET_KEY'),                       // required
    'endpoint'    => env('TRAQIO_ENDPOINT', 'https://api.traqio.app'),
    'timeout'     => (float) env('TRAQIO_TIMEOUT', 10),               // seconds per attempt
    'max_retries' => (int) env('TRAQIO_MAX_RETRIES', 2),              // network error, 429, 5xx
];
MethodWhat it does
signup($userId, $trackId = null, $events = [])Reports the sign-up — the acquisition — with its trackID. The first one is kept.
track($userId, $name, $properties = [])Records one conversion: value, currency, transactionId, store, eventId, trackId.
send($input)The raw call behind both, for several events at once.
TrackId::fromRequest($request = null)The trackID from a Laravel, Symfony or PSR-7 request — or the current PHP request — or null.

In plain PHP the same options are the constructor's second argument: endpoint, timeout, max_retries, and transport to route requests through your own HTTP client.

Proof so far

Tested with PHPUnit — the Laravel integration on Laravel 12 and 13, the client on PHP 8.1 and 8.5 — and run end to end against a running Traqio API: a sign-up carrying a trackID came back matched with its campaign, a second user claiming the same click was told claimed, a wrong key was refused. Not yet run in a customer's production backend.

Frequently asked questions#

Why does $request->cookie('traq_click') return null in Laravel?
Laravel encrypts its cookies and decrypts them on every request; a cookie written by JavaScript — the one @traqio/web sets — fails that decryption, so Laravel hands back null. TrackId::fromRequest reads the raw Cookie header instead, which the decryption never touches. You do not need to add traq_click to the EncryptCookies exceptions.
Which Laravel versions are supported?
Laravel 12 and 13, through package auto-discovery: the service provider, the config file and the Traqio facade register themselves. The client itself is plain PHP 8.1+ and works in Symfony, Slim or any PSR-7 application.
What if the visitor did not come from an ad?
TrackId::fromRequest returns null and the sign-up is still recorded, as organic: no campaign is credited.
Will a retry count a purchase twice?
No. Each call mints its event ids once and retries the identical body, and a purchase is also keyed on its transactionId, so the same payment reported twice is stored once.

Last updated October 16, 2026