AgentDBDocs

Laravel

Call AgentDB from Laravel controllers with the built-in Http client. The key stays in .env and config/services.php. Blade views and your JavaScript get only the data.

Not run in CI. The Laravel code is not run in the test suite. It makes the same AgentDB calls as the crm-php starter, which the smoke test runs.

#Where the key lives

Shell
# .env (never committed)
AGENTDB_API_URL=https://app.agentdb.link
AGENTDB_WORKSPACE=ws_your_workspace_id
AGENTDB_TOKEN=replace-with-your-agent-key
PHP
// config/services.php
'agentdb' => [
    'url' => env('AGENTDB_API_URL', 'https://app.agentdb.link'),
    'workspace' => env('AGENTDB_WORKSPACE'),
    'token' => env('AGENTDB_TOKEN'),
],

Read it with config('services.agentdb.token'). Vite exposes VITE_ variables to the browser: never name the key that way.

#The AgentDB client

PHP
// app/Services/AgentDb.php
namespace App\Services;

use Illuminate\Http\Client\PendingRequest;
use Illuminate\Support\Facades\Http;

class AgentDb
{
    public function client(): PendingRequest
    {
        $c = config('services.agentdb');
        return Http::baseUrl("{$c['url']}/v1/workspaces/{$c['workspace']}")
            ->withToken($c['token'])
            ->acceptJson()
            ->timeout(15);
    }

    public function query(array $query): array
    {
        return $this->client()->post('/query', $query)->throw()->json('data');
    }
}

#The backend route

PHP
// routes/web.php
Route::get('/contacts', [ContactController::class, 'index']);
Route::post('/contacts', [ContactController::class, 'store']);

// app/Http/Controllers/ContactController.php
public function index(Request $request, AgentDb $db)
{
    $q = mb_substr(trim((string) $request->query('q', '')), 0, 100);
    $data = $db->query([
        'object' => 'contact',
        'where' => ['all' => $q === '' ? [] : [['field' => 'name', 'op' => 'contains', 'value' => $q]]],
        'sort' => [['field' => 'name', 'order' => 'asc']],
        'limit' => 50,
    ]);
    return view('contacts.index', ['contacts' => $data['records'], 'q' => $q]);
}

public function store(Request $request, AgentDb $db)
{
    $valid = $request->validate(['name' => 'required|string|max:200', 'email' => 'nullable|email|max:254']);
    $db->client()
        ->withHeaders(['Idempotency-Key' => 'laravel.' . $request->session()->token() . '.' . sha1($valid['name'])])
        ->post('/records', ['object' => 'contact', 'data' => array_filter($valid)])
        ->throw();
    return redirect('/contacts')->with('status', 'Contact added.');
}

Laravel adds CSRF protection to web routes. Keep @csrf in every form.

#The frontend

PHP
{{-- resources/views/contacts/index.blade.php --}}
<form method="get"><input type="search" name="q" value="{{ $q }}"> <button>Search</button></form>
@forelse ($contacts as $c)
  <li>{{ $c['data']['name'] }}</li>
@empty
  <p>No contacts found.</p>
@endforelse
<form method="post" action="/contacts">@csrf <input name="name" required> <button>Add</button></form>

{{ }} escapes values. Do not use {!! !!} with data from AgentDB.

#Loading, empty and error states

Blade pages arrive complete. @forelse … @empty covers the empty state. For AgentDB errors, catch Illuminate\Http\Client\RequestException. Show $e->response->json('error.message') for status 400, and a general message otherwise. A VERSION_CONFLICT (409) means someone saved first.

#Live updates

Poll a JSON route every 10 to 30 seconds from the page, or use a read-only dashboard data link. For push, run a queue worker or a scheduled command that follows the change feed and broadcasts with Laravel's broadcasting. See Live data.