PHP SDK
The Querri PHP SDK (querri/embed) lets you embed Querri analytics into PHP applications. It handles user provisioning, access policy management, embed session creation, and data operations from your server.
Requires PHP 8.3+. Built on Symfony HttpClient.
Installation
Section titled “Installation”composer require querri/embedQuick Start
Section titled “Quick Start”The fastest way to get an embedded analytics session running in a PHP backend:
use Querri\Embed\QuerriClient;
$querri = new QuerriClient([ 'api_key' => $_ENV['QUERRI_API_KEY'], // qk_... 'org_id' => $_ENV['QUERRI_ORG_ID'], // org_...]);
// Create a session for an end user — one call does everything$session = $querri->getSession([ 'user' => [ 'external_id' => $currentUser->id, 'email' => $currentUser->email, 'first_name' => $currentUser->firstName, 'last_name' => $currentUser->lastName, ],]);
// Return the token to your frontendecho json_encode(['sessionToken' => $session->sessionToken]);Then use the Embed SDK on the frontend to render the iframe with that session token.
Authentication
Section titled “Authentication”API keys start with qk_ and are scoped to a single organization. Generate one from Settings > API Keys in the Querri web app.
Every request requires both an API key and an organization ID. You can provide them in three ways:
Option 1: Config array
$querri = new QuerriClient([ 'api_key' => 'qk_live_...', 'org_id' => 'org_...',]);Option 2: API key string only (org ID from environment)
$querri = new QuerriClient('qk_live_...');// Reads QUERRI_ORG_ID from environmentOption 3: Environment variables only
export QUERRI_API_KEY="qk_live_..."export QUERRI_ORG_ID="org_..."$querri = new QuerriClient(); // reads from environmentConfiguration Options
Section titled “Configuration Options”| Parameter | Env Variable | Default | Description |
|---|---|---|---|
api_key | QUERRI_API_KEY | (required) | Your qk_ API key |
org_id | QUERRI_ORG_ID | (required) | Organization ID |
host | QUERRI_URL | https://app.querri.com | Server URL |
timeout | — | 30.0 | Request timeout in seconds |
max_retries | — | 3 | Retry attempts for transient errors |
getSession — The Main Entry Point
Section titled “getSession — The Main Entry Point”getSession() is the flagship method. It orchestrates three API calls into one:
- User resolution — creates or finds the user by
external_id - Access policy — auto-creates/reuses a deterministically-named policy (if inline filters are provided)
- Session creation — returns an embed session token
$session = $querri->getSession([ 'user' => [ 'external_id' => 'user-123', 'email' => 'alice@example.com', 'first_name' => 'Alice', 'last_name' => 'Smith', ], 'access' => [ 'sources' => ['SOURCE_UUID'], 'filters' => ['region' => 'US'], ], 'ttl' => 3600, // session lifetime in seconds (default: 1 hour)]);
// GetSessionResult properties:$session->sessionToken; // pass to the client SDK$session->userId; // Querri's internal user ID$session->externalId; // your external_id echoed back$session->expiresIn; // seconds until expiryThe result implements JsonSerializable, so you can pass it directly to json_encode():
header('Content-Type: application/json');echo json_encode($session);// {"session_token":"es_...","expires_in":3600,"user_id":"...","external_id":"user-123"}User shorthand — if you only need the external ID:
$session = $querri->getSession([ 'user' => 'tenant_123', 'origin' => 'https://app.example.com',]);Access Options
Section titled “Access Options”You can control what data the user sees in two ways:
Inline filters (recommended for most cases) — the SDK auto-manages the policy:
'access' => [ 'sources' => ['source-uuid-1', 'source-uuid-2'], 'filters' => [ 'region' => 'US', // single value 'department' => ['Sales', 'Marketing'], // multiple values (OR) ],],Pre-created policy IDs — when you manage policies yourself:
'access' => [ 'policy_ids' => ['policy-uuid-1', 'policy-uuid-2'],],No access block — the user gets full access to all shared resources (no RLS filtering).
Access Policies
Section titled “Access Policies”If you need more control beyond getSession() auto-management, you can manage policies directly:
Create a policy:
$policy = $querri->policies->create([ 'name' => 'US Region Only', 'description' => 'Only see US rows', 'source_ids' => ['source-uuid'], 'row_filters' => [ ['column' => 'region', 'values' => ['US']], ],]);Assign users to a policy:
$querri->policies->assignUsers($policy['id'], ['user-id-1', 'user-id-2']);Remove a user from a policy:
$querri->policies->removeUser($policyId, $userId);Replace all policy assignments for a user:
$querri->policies->replaceUserPolicies($userId, ['policy-id-1', 'policy-id-2']);// Pass an empty array to remove all policiesResolve what a user can see:
$resolved = $querri->policies->resolve($userId, $sourceId);Discover available columns (for building policy UIs):
$columns = $querri->policies->columns($sourceId);List, retrieve, update, delete:
$policies = $querri->policies->list(['name' => 'US Region']);$policy = $querri->policies->retrieve($policyId);$querri->policies->update($policyId, ['name' => 'New Name']);$querri->policies->del($policyId);How Policies Combine
Section titled “How Policies Combine”- Same column, multiple policies = OR. Policies for
region = USandregion = EUmeans the user sees US or EU rows. - Different columns = AND. Policies for
region = USanddepartment = Salesmeans only rows matching both. - No policies assigned = full access (permissive default).
User Management
Section titled “User Management”// Create a user$user = $querri->users->create([ 'email' => 'alice@example.com', 'external_id' => 'cust-42', 'first_name' => 'Alice', 'last_name' => 'Smith', 'role' => 'member',]);
// Get or create by external ID (idempotent)$user = $querri->users->getOrCreate('cust-42', [ 'email' => 'alice@example.com', 'first_name' => 'Alice',]);
// Retrieve by Querri user ID$user = $querri->users->retrieve($userId);
// List users$page = $querri->users->list(['limit' => 50]);
// Filter by external ID$page = $querri->users->list(['external_id' => 'cust-42']);
// Update a user$querri->users->update($userId, ['role' => 'admin']);
// Delete a user$querri->users->del($userId);
// Remove an external ID mapping (without deleting the user)$querri->users->removeExternalId('cust-42');Embed Sessions
Section titled “Embed Sessions”If you need lower-level session control beyond getSession():
// Create a session directly$session = $querri->embed->createSession([ 'user_id' => $userId, 'origin' => 'https://myapp.com', 'ttl' => 7200,]);
// Refresh before expiry$refreshed = $querri->embed->refreshSession($session['session_token']);
// List active sessions$sessions = $querri->embed->listSessions(['limit' => 50]);
// Revoke a specific session$querri->embed->revokeSession($sessionId);
// Revoke all sessions for a user$querri->embed->revokeUserSessions($userId);User-Scoped Client
Section titled “User-Scoped Client”After creating a session, you can create a client that acts as the user. Resources are FGA-filtered — only data the user has access to is returned:
$session = $querri->getSession(['user' => 'ext-123']);$userClient = $querri->asUser($session);
// These calls are permission-filtered$projects = $userClient->projects->list();$dashboards = $userClient->dashboards->list();$sources = $userClient->sources->list();Sharing
Section titled “Sharing”Grant access to resources programmatically:
// Share a project with a user$querri->sharing->shareProject($projectId, [ 'user_id' => $userId, 'permission' => 'view', // "view" or "edit"]);
// Share a dashboard$querri->sharing->shareDashboard($dashboardId, [ 'user_id' => $userId, 'permission' => 'view',]);
// Enable org-wide sharing for a data source$querri->sharing->orgShareSource($sourceId, ['enabled' => true]);
// Share a source with a specific user$querri->sharing->shareSource($sourceId, [ 'user_id' => $userId, 'permission' => 'view',]);
// List and revoke shares$querri->sharing->listProjectShares($projectId);$querri->sharing->revokeProjectShare($projectId, $userId);
// List and revoke dashboard shares$querri->sharing->listDashboardShares($dashboardId);$querri->sharing->revokeDashboardShare($dashboardId, $userId);Error Handling
Section titled “Error Handling”The SDK throws typed exceptions for different failure modes:
use Querri\Embed\Exceptions\AuthenticationException;use Querri\Embed\Exceptions\NotFoundException;use Querri\Embed\Exceptions\RateLimitException;use Querri\Embed\Exceptions\ValidationException;use Querri\Embed\Exceptions\ServerException;use Querri\Embed\Exceptions\QuerriException;
try { $session = $querri->getSession(['user' => 'ext-123']);} catch (AuthenticationException $e) { // 401 — Invalid API key} catch (RateLimitException $e) { // 429 — Back off and retry} catch (NotFoundException $e) { // 404 — Resource not found} catch (ValidationException $e) { // 400 — Bad request parameters} catch (ServerException $e) { // 5xx — Server error} catch (QuerriException $e) { // Catch-all for any SDK error}Exception Hierarchy
Section titled “Exception Hierarchy”QuerriException├── ConfigException — missing/invalid configuration├── ConnectionException — network failures (auto-retried)│ └── TimeoutException — request timeout exceeded└── ApiException — HTTP error responses ├── ValidationException — 400 ├── AuthenticationException — 401 ├── PermissionException — 403 ├── NotFoundException — 404 ├── ConflictException — 409 ├── RateLimitException — 429 (auto-retried) └── ServerException — 5xx (auto-retried)The SDK automatically retries on 429 and 5xx errors (up to maxRetries, default 3).
Complete Example: Multi-Tenant Embed with RLS
Section titled “Complete Example: Multi-Tenant Embed with RLS”This example shows a Laravel route that creates a per-tenant embed session:
use Querri\Embed\QuerriClient;
// Initialize once (e.g., in a service provider)$querri = new QuerriClient([ 'api_key' => config('services.querri.api_key'), 'org_id' => config('services.querri.org_id'),]);
// In your controllerRoute::get('/api/querri-token', function (Request $request) use ($querri) { $user = $request->user();
$session = $querri->getSession([ 'user' => [ 'external_id' => (string) $user->id, 'email' => $user->email, 'first_name' => $user->first_name, 'last_name' => $user->last_name, ], 'access' => [ 'sources' => [config('services.querri.source_id')], 'filters' => ['tenant_id' => (string) $user->tenant_id], ], ]);
return response()->json($session);});Each tenant sees only their rows. One dataset, one embed, automatic filtering.
Security Best Practices
Section titled “Security Best Practices”- API keys (
qk_*) should only be used server-side — never expose them in client code or version control - Session tokens (
es_*) are safe for the browser — they are scoped and time-limited - Set
originingetSession()to restrict which domains can use the session token - Use environment variables for credentials (
QUERRI_API_KEY,QUERRI_ORG_ID) - Rotate API keys periodically via Settings > API Keys
API Reference
Section titled “API Reference”The PHP SDK wraps the Querri V1 API endpoints. For the complete endpoint reference, see API Reference.
| SDK Resource | API Endpoints |
|---|---|
$querri->users | /v1/users/* |
$querri->embed | /v1/embed/sessions/* |
$querri->policies | /v1/access/policies/*, /v1/access/resolve, /v1/access/columns |
$querri->files | /v1/files/* |
$querri->projects | /v1/projects/* |
$querri->dashboards | /v1/dashboards/* |
$querri->data | /v1/data/* |
$querri->sharing | /v1/projects/*/shares, /v1/dashboards/*/shares, /v1/sources/*/shares |
$querri->sources | /v1/sources/* |
$querri->keys | /v1/keys/* |
$querri->audit | /v1/audit/* |
$querri->usage | /v1/usage/* |