refinder/laravel-sdk

Official Laravel SDK for Refinder AI Tools — SEO automation and AI-powered content optimization.

Installs: 5

Dependents: 0

Suggesters: 0

Security: 0

Stars: 0

Watchers: 0

Forks: 0

Open Issues: 0

pkg:composer/refinder/laravel-sdk

v1.0.0 2026-02-08 11:05 UTC

This package is auto-updated.

Last update: 2026-02-10 15:53:59 UTC


README

Official Laravel SDK for Refinder AI Tools
SEO automation and AI-powered content optimization for Laravel applications.

InstallationQuick StartSEO ToolAPI ReferenceError HandlingTesting

Overview

The Refinder Laravel SDK is a first-party Laravel package that integrates your application with the Refinder AI Tools platform. It provides:

  • A clean Facade (Refinder) for quick access to all features
  • Fully typed DTOs for all requests and responses
  • Automatic authentication via API key (X-API-Key header)
  • Built-in error handling with a custom exception hierarchy
  • Event dispatching for tool executions (success & failure)
  • Caching support for identical tool inputs
  • Usage tracking helpers for monitoring quotas
  • Full IDE autocompletion support via PHPDoc annotations

What Does the Refinder API Do?

Refinder AI Tools is an AI middleware platform that provides AI-powered tools as a service:

  • Abstracts multiple LLM providers (OpenAI, DeepSeek, Alibaba Cloud Qwen, etc.) behind a unified interface
  • Provides pluggable AI tools (starting with SEO)
  • Manages subscriptions, usage quotas, and rate limiting
  • Returns structured JSON outputs from all tools

Requirements

Requirement Version
PHP >= 8.2
Laravel 11.x or 12.x
Guzzle >= 7.0

Installation

composer require refinder/laravel-sdk

The package auto-registers its service provider and facade via Laravel's package discovery.

Publish Configuration

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

This creates config/refinder.php with all available options.

Environment Variables

Add the following to your .env file:

REFINDER_API_KEY=rfnd_your_api_key_here
REFINDER_BASE_URL=https://api.refinder.ai/api/v1
REFINDER_TIMEOUT=120
REFINDER_RETRIES=2
REFINDER_CACHE_ENABLED=false
REFINDER_CACHE_TTL=60
REFINDER_EVENTS=true

Required: Only REFINDER_API_KEY is required. All other values have sensible defaults.

Quick Start

use Refinder\LaravelSdk\Facades\Refinder;

// Execute SEO analysis
$result = Refinder::seo()->execute([
    'content' => 'Your article content here...',
    'content_type' => 'article',
    'language' => 'en',
]);

// Access the results
echo $result->output->metaTitle;           // "Optimized SEO Title..."
echo $result->output->metaDescription;     // "Meta description..."
echo $result->output->seoScore->overall;   // 75 (0-100)

// Check if score is passing (>= 60)
if ($result->output->seoScore->isPassing()) {
    echo "SEO score is good!";
}

Configuration

The full configuration file (config/refinder.php):

return [
    // Your API key (required)
    'api_key' => env('REFINDER_API_KEY'),

    // API base URL
    'base_url' => env('REFINDER_BASE_URL', 'https://api.refinder.ai/api/v1'),

    // Request timeout in seconds (tool executions can take 10-30s)
    'timeout' => env('REFINDER_TIMEOUT', 120),

    // Retry on 5xx/timeout errors (never retries 4xx)
    'retries' => env('REFINDER_RETRIES', 2),
    'retry_delay_ms' => env('REFINDER_RETRY_DELAY', 1000),

    // Response caching for identical inputs
    'cache' => [
        'enabled' => env('REFINDER_CACHE_ENABLED', false),
        'ttl' => env('REFINDER_CACHE_TTL', 60),   // minutes
        'prefix' => 'refinder_',
    ],

    // Dispatch Laravel events after tool executions
    'events' => env('REFINDER_EVENTS', true),

    // Log channel (null = default channel)
    'log_channel' => env('REFINDER_LOG_CHANNEL', null),
];

SEO Tool

The SEO tool is the primary feature of the Refinder platform. It analyzes content and generates comprehensive SEO recommendations.

Input Parameters

Parameter Type Required Default Description
content string Yes Text content to analyze (min 10 chars)
url string No null Page URL for additional context
content_type string No "page" One of: article, product, service, page, blog
language string No "en" Target language code (e.g., en, ar, fr)
depth string No "advanced" Analysis depth: basic, advanced, technical
target_keywords array No [] Specific keywords to optimize around
brand_name string No null Brand name for meta titles/headings
industry string No null Industry context for better optimization

Depth Levels

Level Includes
basic Meta title, meta description, primary keywords
advanced Everything in basic + secondary/long-tail keywords, content analysis, heading suggestions, optimization suggestions, SEO scores
technical Everything in advanced + schema markup, Open Graph tags, canonical URLs, content gap analysis

Using the SeoInput DTO

use Refinder\LaravelSdk\DTOs\SeoInput;
use Refinder\LaravelSdk\Facades\Refinder;

$input = new SeoInput(
    content: $article->body,
    url: route('articles.show', $article),
    contentType: 'article',
    language: 'en',
    depth: 'advanced',
    targetKeywords: ['AI tools', 'machine learning'],
    brandName: 'MyBrand',
    industry: 'Technology',
);

$result = Refinder::seo()->execute($input);

Shorthand Methods

// Basic analysis
$result = Refinder::seo()->basic($content, 'en');

// Advanced analysis (default)
$result = Refinder::seo()->advanced($content, 'en');

// Full technical analysis
$result = Refinder::seo()->technical($content, 'en');

Working with SEO Output

$result = Refinder::seo()->execute(['content' => $text]);
$seo = $result->output;

// Meta tags
echo $seo->metaTitle;           // string (50-60 chars)
echo $seo->metaDescription;     // string (150-160 chars)

// Keywords
$seo->keywords->primary;        // ['keyword1', 'keyword2', ...]
$seo->keywords->secondary;      // ['keyword3', 'keyword4', ...]
$seo->keywords->longTail;       // ['long tail phrase', ...]
$seo->keywords->all();          // All keywords merged

// Content Analysis
$seo->contentAnalysis->readabilityScore;    // 'excellent', 'good', 'needs_improvement'
$seo->contentAnalysis->wordCountAssessment; // string
$seo->contentAnalysis->contentQuality;      // 'high', 'medium', 'low'
$seo->contentAnalysis->keyTopicsCovered;    // ['topic1', ...]

// SEO Score (0-100)
$seo->seoScore->overall;    // 75
$seo->seoScore->content;    // 70
$seo->seoScore->keywords;   // 80
$seo->seoScore->structure;  // 75
$seo->seoScore->isPassing(); // true (>= 60)

// Optimization Suggestions
foreach ($seo->optimizationSuggestions as $suggestion) {
    echo $suggestion->category;    // 'content', 'technical', 'structure', 'keywords'
    echo $suggestion->priority;    // 'high', 'medium', 'low'
    echo $suggestion->suggestion;  // "Add structured data markup"
    echo $suggestion->details;     // "Implementing JSON-LD..."
    $suggestion->isHighPriority(); // bool
}

// Heading Structure
echo $seo->headingStructure->suggestedH1;  // string
$seo->headingStructure->suggestedH2s;      // array of strings
$seo->headingStructure->suggestedH3s;      // array of strings

// Execution metadata
echo $result->id;                        // UUID
echo $result->status;                    // 'completed'
echo $result->usage->totalTokens;        // 1276
echo $result->usage->executionTimeSeconds(); // 14.5
echo $result->model->name;              // 'Qwen Plus'
echo $result->model->provider;          // 'alibaba'

API Reference

Platform Information

$platform = Refinder::me();

echo $platform->id;           // 1
echo $platform->name;         // "My Platform"
echo $platform->slug;         // "my-platform"
echo $platform->website;      // "https://example.com"
echo $platform->description;  // "Platform description"
echo $platform->isActive;     // true
echo $platform->createdAt;    // "2026-02-08T09:00:00+00:00"

Subscription Details

$sub = Refinder::subscription();

echo $sub->plan->name;                // "Pro"
echo $sub->plan->slug;                // "pro"
echo $sub->plan->maxRequestsPerMonth; // 1000
echo $sub->plan->maxRequestsPerDay;   // 100
echo $sub->plan->features;            // ['advanced_seo', 'api_access', ...]
echo $sub->status;                     // "active"
$sub->isActive();                      // true
$sub->isOnTrial();                     // false

Usage Statistics

// Current month usage
$usage = Refinder::usage();

// Custom date range
$usage = Refinder::usage('2026-01-01', '2026-01-31');

echo $usage->totalRequests; // 47
echo $usage->totalTokens;   // 62340
echo $usage->totalCost;     // 0.12468
echo $usage->periodFrom;    // "2026-02-01"
echo $usage->periodTo;      // "2026-02-08"

// Daily breakdown
foreach ($usage->daily as $day) {
    echo $day->date;           // "2026-02-08"
    echo $day->tool;           // "seo"
    echo $day->requestsCount;  // 12
    echo $day->tokensUsed;     // 15600
    echo $day->estimatedCost;  // 0.0312
}

Execution History

// List executions (paginated)
$executions = Refinder::executions(perPage: 10, page: 1);

echo $executions->total;        // 47
echo $executions->currentPage;  // 1
echo $executions->lastPage;     // 5
echo $executions->hasMorePages(); // true

foreach ($executions->items as $exec) {
    echo $exec->id;                    // UUID
    echo $exec->tool;                  // "seo"
    echo $exec->status;                // "completed"
    echo $exec->usage->totalTokens;    // 1276
    echo $exec->createdAt;             // ISO 8601 string
}

// Get specific execution
$exec = Refinder::execution('d47181d4-d26b-4224-a7f7-4c973b66f8fa');
if ($exec->isCompleted()) {
    echo $exec->output->metaTitle;
}

Available Tools

$tools = Refinder::tools();
// Returns array of tool definitions

Error Handling

The SDK throws specific exception types for different error scenarios:

RefinderException (base)
├── AuthenticationException    (HTTP 401)
├── SubscriptionException      (HTTP 403)
├── ValidationException        (HTTP 422)
├── RateLimitException         (HTTP 429)
├── ToolException              (HTTP 500/502)
└── ConnectionException        (Network errors)

Exception Properties

Exception Extra Properties
RefinderException errorCode, httpStatus
ValidationException errors (array of field errors)
RateLimitException limit, current
ToolException executionId (UUID for debugging)

Error Handling Example

use Refinder\LaravelSdk\Exceptions\AuthenticationException;
use Refinder\LaravelSdk\Exceptions\RateLimitException;
use Refinder\LaravelSdk\Exceptions\SubscriptionException;
use Refinder\LaravelSdk\Exceptions\ToolException;
use Refinder\LaravelSdk\Exceptions\ValidationException;
use Refinder\LaravelSdk\Facades\Refinder;

try {
    $result = Refinder::seo()->execute(['content' => $text]);

    return response()->json([
        'seo' => $result->output,
        'tokens' => $result->usage->totalTokens,
    ]);

} catch (ValidationException $e) {
    // Input validation failed (422)
    return response()->json(['error' => $e->errors], 422);

} catch (RateLimitException $e) {
    // Quota exceeded (429)
    return response()->json([
        'error' => 'Rate limit reached',
        'limit' => $e->limit,
        'current' => $e->current,
    ], 429);

} catch (SubscriptionException $e) {
    // Subscription issue (403)
    return response()->json(['error' => $e->getMessage()], 403);

} catch (AuthenticationException $e) {
    // Invalid API key (401)
    Log::critical('Refinder API key is invalid!', ['code' => $e->errorCode]);
    return response()->json(['error' => 'Service unavailable'], 503);

} catch (ToolException $e) {
    // AI processing failed (500/502)
    Log::error('Tool failed', ['execution_id' => $e->executionId]);
    return response()->json(['error' => 'Please retry'], 502);
}

Error Codes Reference

HTTP Code Description
401 MISSING_API_KEY No API key in request header
401 INVALID_API_KEY API key doesn't match any active key
401 API_KEY_EXPIRED API key has expired
403 PLATFORM_INACTIVE Platform is disabled
403 NO_ACTIVE_SUBSCRIPTION No active subscription
403 SUBSCRIPTION_EXPIRED Subscription has expired
403 TOOL_NOT_ALLOWED Plan doesn't include this tool
404 TOOL_NOT_FOUND Tool slug doesn't exist
404 EXECUTION_NOT_FOUND Execution UUID not found
422 VALIDATION_ERROR Input validation failed
429 DAILY_LIMIT_EXCEEDED Daily quota exceeded
429 MONTHLY_LIMIT_EXCEEDED Monthly quota exceeded
500 TOOL_ERROR Tool execution error
502 LLM_ERROR AI provider returned an error

Events

When config('refinder.events') is true (default), the package dispatches Laravel events:

ToolExecuted

Dispatched after a successful tool execution:

use Refinder\LaravelSdk\Events\ToolExecuted;

Event::listen(ToolExecuted::class, function (ToolExecuted $event) {
    Log::info("Refinder tool executed", [
        'tool' => $event->tool,           // "seo"
        'execution_id' => $event->executionId,
        'tokens' => $event->tokensUsed,
        'time_ms' => $event->executionTimeMs,
    ]);
});

ToolExecutionFailed

Dispatched after a failed tool execution:

use Refinder\LaravelSdk\Events\ToolExecutionFailed;

Event::listen(ToolExecutionFailed::class, function (ToolExecutionFailed $event) {
    Log::error("Refinder tool failed", [
        'tool' => $event->tool,
        'error_code' => $event->errorCode,
        'message' => $event->errorMessage,
        'execution_id' => $event->executionId,
    ]);
});

RateLimitApproaching

Dispatched when usage approaches the limit:

use Refinder\LaravelSdk\Events\RateLimitApproaching;

Event::listen(RateLimitApproaching::class, function (RateLimitApproaching $event) {
    Log::warning("Approaching rate limit", [
        'type' => $event->limitType,       // "daily" or "monthly"
        'limit' => $event->limit,
        'current' => $event->current,
        'percentage' => $event->percentageUsed,
    ]);
});

Caching

Enable response caching for identical tool inputs:

REFINDER_CACHE_ENABLED=true
REFINDER_CACHE_TTL=60
  • Uses Laravel's default cache driver
  • Cache keys are generated from prefix + tool_slug + md5(input)
  • Only successful executions are cached (never failures)
  • TTL is in minutes

Rate Limiting & Quotas

The Refinder API enforces quotas based on your subscription plan:

Limit Reset Period
Daily Midnight UTC
Monthly 1st of month

Checking Quota Usage

$usage = Refinder::usage();
$sub = Refinder::subscription();

$used = $usage->totalRequests;
$limit = $sub->plan->maxRequestsPerMonth;
$remaining = $limit - $used;
$percentage = ($used / $limit) * 100;

echo "Used {$used}/{$limit} ({$percentage}%) this month.";
echo "{$remaining} requests remaining.";

Testing

The SDK provides a RefinderFake class for testing:

Basic Fake

use Refinder\LaravelSdk\Facades\Refinder;

public function test_seo_analysis()
{
    Refinder::fake();

    // Your code calls the SDK as normal
    $result = Refinder::seo()->execute(['content' => 'Test content for SEO']);

    // Results come from the fake
    $this->assertEquals('completed', $result->status);
    $this->assertNotNull($result->output->metaTitle);
    $this->assertTrue($result->output->seoScore->isPassing());

    // Assert the tool was called
    Refinder::assertToolExecuted('seo');
    Refinder::assertToolExecutedCount('seo', 1);
}

Custom Fake Responses

Refinder::fake();

Refinder::fakeToolExecution('seo', [
    'meta_title' => 'Custom Test Title',
    'meta_description' => 'Custom test description.',
    'keywords' => [
        'primary' => ['custom keyword'],
        'secondary' => ['test'],
        'long_tail' => ['custom long tail'],
    ],
    'content_analysis' => [
        'readability_score' => 'excellent',
        'word_count_assessment' => 'Perfect length.',
        'content_quality' => 'high',
        'key_topics_covered' => ['testing'],
    ],
    'optimization_suggestions' => [],
    'heading_structure' => [
        'suggested_h1' => 'Custom H1',
        'suggested_h2s' => ['Custom H2'],
        'suggested_h3s' => [],
    ],
    'seo_score' => [
        'overall' => 95,
        'content' => 90,
        'keywords' => 95,
        'structure' => 95,
    ],
]);

$result = Refinder::seo()->execute(['content' => 'Any content']);
$this->assertEquals('Custom Test Title', $result->output->metaTitle);
$this->assertEquals(95, $result->output->seoScore->overall);

Assertions

Refinder::fake();

// Assert tool was executed
Refinder::assertToolExecuted('seo');

// Assert exact count
Refinder::assertToolExecutedCount('seo', 3);

// Assert nothing was executed
Refinder::assertNothingExecuted();

Real-World Examples

E-Commerce Product SEO

$product = Product::find(1);

$result = Refinder::seo()->execute([
    'content' => $product->description,
    'url' => route('products.show', $product),
    'content_type' => 'product',
    'brand_name' => $product->brand->name,
    'industry' => $product->category->name,
    'target_keywords' => $product->tags->pluck('name')->toArray(),
]);

if ($result->isCompleted()) {
    $product->seoMeta()->updateOrCreate([], [
        'title' => $result->output->metaTitle,
        'description' => $result->output->metaDescription,
        'keywords' => $result->output->keywords->all(),
        'h1' => $result->output->headingStructure->suggestedH1,
        'score' => $result->output->seoScore->overall,
        'analyzed_at' => now(),
    ]);
}

Batch Processing with Queue

use Refinder\LaravelSdk\Facades\Refinder;
use Refinder\LaravelSdk\Exceptions\RateLimitException;

class AnalyzeArticleSeo implements ShouldQueue
{
    public int $tries = 3;
    public int $backoff = 60;

    public function __construct(public Article $article) {}

    public function handle(): void
    {
        try {
            $result = Refinder::seo()->execute([
                'content' => $this->article->body,
                'content_type' => 'article',
                'language' => $this->article->locale,
                'depth' => 'advanced',
            ]);

            $this->article->update([
                'seo_title' => $result->output->metaTitle,
                'seo_description' => $result->output->metaDescription,
                'seo_score' => $result->output->seoScore->overall,
                'seo_analyzed_at' => now(),
            ]);
        } catch (RateLimitException $e) {
            $this->release(300); // retry in 5 minutes
        }
    }
}

// Dispatch for all unanalyzed articles
Article::whereNull('seo_analyzed_at')->chunk(50, function ($articles) {
    foreach ($articles as $article) {
        AnalyzeArticleSeo::dispatch($article);
    }
});

Admin Dashboard Integration

// Get platform info
$platform = Refinder::me();
echo "Platform: {$platform->name} | Active: " . ($platform->isActive ? 'Yes' : 'No');

// Get subscription
$sub = Refinder::subscription();
echo "Plan: {$sub->plan->name} | Monthly: {$sub->plan->maxRequestsPerMonth}";

// Get usage
$usage = Refinder::usage();
echo "Requests: {$usage->totalRequests} | Tokens: {$usage->totalTokens}";
echo "Cost: \${$usage->totalCost}";

// Browse execution history
$executions = Refinder::executions(perPage: 10);
foreach ($executions->items as $exec) {
    echo "{$exec->id} | {$exec->tool} | {$exec->status} | {$exec->usage->totalTokens} tokens";
}

Package Structure

refinder-laravel-sdk/
├── src/
│   ├── RefinderServiceProvider.php      # Auto-discovered service provider
│   ├── RefinderClient.php               # Core HTTP client
│   ├── RefinderManager.php              # Main manager (Facade target)
│   ├── Facades/
│   │   └── Refinder.php                 # Facade with fake() support
│   ├── Tools/
│   │   ├── BaseTool.php                 # Abstract base with caching/events
│   │   └── SeoTool.php                  # SEO tool wrapper
│   ├── DTOs/
│   │   ├── SeoInput.php                 # Input DTO
│   │   ├── SeoOutput.php                # Full output DTO
│   │   ├── SeoKeywords.php              # Keywords container
│   │   ├── SeoScore.php                 # Score with isPassing()
│   │   ├── SeoSuggestion.php            # Single suggestion
│   │   ├── SeoContentAnalysis.php       # Content analysis data
│   │   ├── SeoHeadingStructure.php      # Heading suggestions
│   │   ├── ToolExecution.php            # Execution result wrapper
│   │   ├── ExecutionUsage.php           # Token/time usage
│   │   ├── ExecutionModel.php           # AI model info
│   │   ├── PlatformInfo.php             # Platform data
│   │   ├── SubscriptionInfo.php         # Subscription details
│   │   ├── SubscriptionPlan.php         # Plan limits/features
│   │   ├── UsageSummary.php             # Usage aggregation
│   │   ├── UsageDay.php                 # Daily usage entry
│   │   └── PaginatedResult.php          # Paginated list wrapper
│   ├── Exceptions/
│   │   ├── RefinderException.php        # Base exception
│   │   ├── AuthenticationException.php  # 401 errors
│   │   ├── SubscriptionException.php    # 403 errors
│   │   ├── ValidationException.php      # 422 errors
│   │   ├── RateLimitException.php       # 429 errors
│   │   ├── ToolException.php            # 500/502 errors
│   │   └── ConnectionException.php      # Network errors
│   ├── Events/
│   │   ├── ToolExecuted.php             # Success event
│   │   ├── ToolExecutionFailed.php      # Failure event
│   │   └── RateLimitApproaching.php     # Quota warning
│   ├── Contracts/
│   │   └── RefinderClientInterface.php  # HTTP client interface
│   └── Testing/
│       ├── RefinderFake.php             # Fake manager for tests
│       └── SeoToolFake.php              # Fake SEO tool
├── config/
│   └── refinder.php                     # Configuration file
├── composer.json
├── README.md
├── LICENSE
└── CHANGELOG.md

Authentication

Every request to the Refinder API includes the platform's API key automatically:

X-API-Key: rfnd_XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
Property Value
Prefix rfnd_
Total length 53 characters
Format rfnd_ + 48 alphanumeric characters

The API key is read from config('refinder.api_key') and attached to every HTTP request by the RefinderClient.

Contributing

Contributions are welcome! Please see CONTRIBUTING.md for details.

Security

If you discover a security vulnerability, please send an email to security@refinder.ai instead of using the issue tracker.

License

The MIT License (MIT). Please see LICENSE for more information.

Built with care by the Refinder AI Tools team.