fix: replace CDN Alpine with Flux directives, rebrand API portal
Some checks failed
CI / PHP 8.3 (push) Failing after 2s
CI / PHP 8.4 (push) Failing after 2s

- Remove CDN Alpine.js from layout (conflicted with Flux's bundled Alpine)
- Add @fluxAppearance and @fluxScripts for proper Flux UI rendering
- Replace all hardcoded "Host UK" with config('core.app.name')
- Migrate colour scheme from slate to zinc, blue to cyan (match MCP portal)
- Rewrite index/reference/quickstart with brain/score API endpoints
- Fix broken route('api.guides.biolinks') references causing 500 errors
- Replace api.host.uk.com URLs with api.lthn.ai

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Snider 2026-03-03 20:50:44 +00:00
parent e709545db1
commit 774ecae403
14 changed files with 592 additions and 739 deletions

View file

@ -3,7 +3,7 @@
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Host UK API Documentation</title>
<title>{{ config('core.app.name', 'Core PHP') }} API Documentation</title>
<link rel="stylesheet" href="https://unpkg.com/swagger-ui-dist@5/swagger-ui.css">
<style>
html {
@ -72,10 +72,10 @@
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" d="M17.25 6.75 22.5 12l-5.25 5.25m-10.5 0L1.5 12l5.25-5.25m7.5-3-4.5 16.5" />
</svg>
Host UK API
{{ config('core.app.name', 'Core PHP') }} API
</h1>
<nav class="api-header-links">
<a href="{{ config('app.url') }}">Host UK</a>
<a href="{{ config('app.url') }}">{{ config('core.app.name', 'Core PHP') }}</a>
<a href="/openapi.json" target="_blank">OpenAPI JSON</a>
<a href="{{ str_replace('api.', 'mcp.', request()->getSchemeAndHttpHost()) }}">MCP Portal</a>
</nav>

View file

@ -6,32 +6,32 @@
<div class="flex">
{{-- Sidebar --}}
<aside class="hidden lg:block fixed left-0 top-16 md:top-20 bottom-0 w-64 border-r border-slate-200 dark:border-slate-800">
<aside class="hidden lg:block fixed left-0 top-16 md:top-20 bottom-0 w-64 border-r border-zinc-200 dark:border-zinc-800">
<div class="h-full px-4 py-8 overflow-y-auto no-scrollbar">
<nav>
<ul class="space-y-2">
<li>
<a href="#overview" data-scrollspy-link class="block px-3 py-2 text-sm text-slate-600 hover:text-slate-900 dark:text-slate-400 dark:hover:text-slate-200 rounded-sm relative before:absolute before:inset-y-1 before:left-0 before:w-0.5 before:rounded-full">
<a href="#overview" data-scrollspy-link class="block px-3 py-2 text-sm text-zinc-600 hover:text-zinc-900 dark:text-zinc-400 dark:hover:text-zinc-200 rounded-sm relative before:absolute before:inset-y-1 before:left-0 before:w-0.5 before:rounded-full">
Overview
</a>
</li>
<li>
<a href="#api-keys" data-scrollspy-link class="block px-3 py-2 text-sm text-slate-600 hover:text-slate-900 dark:text-slate-400 dark:hover:text-slate-200 rounded-sm relative before:absolute before:inset-y-1 before:left-0 before:w-0.5 before:rounded-full">
<a href="#api-keys" data-scrollspy-link class="block px-3 py-2 text-sm text-zinc-600 hover:text-zinc-900 dark:text-zinc-400 dark:hover:text-zinc-200 rounded-sm relative before:absolute before:inset-y-1 before:left-0 before:w-0.5 before:rounded-full">
API Keys
</a>
</li>
<li>
<a href="#using-keys" data-scrollspy-link class="block px-3 py-2 text-sm text-slate-600 hover:text-slate-900 dark:text-slate-400 dark:hover:text-slate-200 rounded-sm relative before:absolute before:inset-y-1 before:left-0 before:w-0.5 before:rounded-full">
<a href="#using-keys" data-scrollspy-link class="block px-3 py-2 text-sm text-zinc-600 hover:text-zinc-900 dark:text-zinc-400 dark:hover:text-zinc-200 rounded-sm relative before:absolute before:inset-y-1 before:left-0 before:w-0.5 before:rounded-full">
Using API Keys
</a>
</li>
<li>
<a href="#scopes" data-scrollspy-link class="block px-3 py-2 text-sm text-slate-600 hover:text-slate-900 dark:text-slate-400 dark:hover:text-slate-200 rounded-sm relative before:absolute before:inset-y-1 before:left-0 before:w-0.5 before:rounded-full">
<a href="#scopes" data-scrollspy-link class="block px-3 py-2 text-sm text-zinc-600 hover:text-zinc-900 dark:text-zinc-400 dark:hover:text-zinc-200 rounded-sm relative before:absolute before:inset-y-1 before:left-0 before:w-0.5 before:rounded-full">
Scopes
</a>
</li>
<li>
<a href="#security" data-scrollspy-link class="block px-3 py-2 text-sm text-slate-600 hover:text-slate-900 dark:text-slate-400 dark:hover:text-slate-200 rounded-sm relative before:absolute before:inset-y-1 before:left-0 before:w-0.5 before:rounded-full">
<a href="#security" data-scrollspy-link class="block px-3 py-2 text-sm text-zinc-600 hover:text-zinc-900 dark:text-zinc-400 dark:hover:text-zinc-200 rounded-sm relative before:absolute before:inset-y-1 before:left-0 before:w-0.5 before:rounded-full">
Security Best Practices
</a>
</li>
@ -47,35 +47,35 @@
{{-- Breadcrumb --}}
<nav class="mb-8">
<ol class="flex items-center gap-2 text-sm">
<li><a href="{{ route('api.guides') }}" class="text-slate-500 hover:text-slate-700 dark:text-slate-400 dark:hover:text-slate-200">Guides</a></li>
<li class="text-slate-400">/</li>
<li class="text-slate-800 dark:text-slate-200">Authentication</li>
<li><a href="{{ route('api.guides') }}" class="text-zinc-500 hover:text-zinc-700 dark:text-zinc-400 dark:hover:text-zinc-200">Guides</a></li>
<li class="text-zinc-400">/</li>
<li class="text-zinc-800 dark:text-zinc-200">Authentication</li>
</ol>
</nav>
<h1 class="h1 mb-4 text-slate-800 dark:text-slate-100">Authentication</h1>
<p class="text-xl text-slate-600 dark:text-slate-400 mb-12">
<h1 class="h1 mb-4 text-zinc-800 dark:text-zinc-100">Authentication</h1>
<p class="text-xl text-zinc-600 dark:text-zinc-400 mb-12">
Learn how to authenticate your API requests using API keys.
</p>
{{-- Overview --}}
<section id="overview" data-scrollspy-target class="mb-12">
<h2 class="h3 mb-4 text-slate-800 dark:text-slate-100">Overview</h2>
<p class="text-slate-600 dark:text-slate-400 mb-4">
<h2 class="h3 mb-4 text-zinc-800 dark:text-zinc-100">Overview</h2>
<p class="text-zinc-600 dark:text-zinc-400 mb-4">
The API uses API keys for authentication. Each API key is scoped to a specific workspace and has configurable permissions.
</p>
<p class="text-slate-600 dark:text-slate-400">
API keys are prefixed with <code class="px-1.5 py-0.5 bg-slate-100 dark:bg-slate-800 rounded text-sm">hk_</code> to make them easily identifiable.
<p class="text-zinc-600 dark:text-zinc-400">
API keys are prefixed with <code class="px-1.5 py-0.5 bg-zinc-100 dark:bg-zinc-800 rounded text-sm">hk_</code> to make them easily identifiable.
</p>
</section>
{{-- API Keys --}}
<section id="api-keys" data-scrollspy-target class="mb-12">
<h2 class="h3 mb-4 text-slate-800 dark:text-slate-100">API Keys</h2>
<p class="text-slate-600 dark:text-slate-400 mb-4">
<h2 class="h3 mb-4 text-zinc-800 dark:text-zinc-100">API Keys</h2>
<p class="text-zinc-600 dark:text-zinc-400 mb-4">
To create an API key:
</p>
<ol class="list-decimal list-inside space-y-2 text-slate-600 dark:text-slate-400 mb-6">
<ol class="list-decimal list-inside space-y-2 text-zinc-600 dark:text-zinc-400 mb-6">
<li>Log in to your account</li>
<li>Navigate to <strong>Settings API Keys</strong></li>
<li>Click <strong>Create API Key</strong></li>
@ -98,59 +98,59 @@
{{-- Using Keys --}}
<section id="using-keys" data-scrollspy-target class="mb-12">
<h2 class="h3 mb-4 text-slate-800 dark:text-slate-100">Using API Keys</h2>
<p class="text-slate-600 dark:text-slate-400 mb-4">
Include your API key in the <code class="px-1.5 py-0.5 bg-slate-100 dark:bg-slate-800 rounded text-sm">Authorization</code> header as a Bearer token:
<h2 class="h3 mb-4 text-zinc-800 dark:text-zinc-100">Using API Keys</h2>
<p class="text-zinc-600 dark:text-zinc-400 mb-4">
Include your API key in the <code class="px-1.5 py-0.5 bg-zinc-100 dark:bg-zinc-800 rounded text-sm">Authorization</code> header as a Bearer token:
</p>
<div class="bg-slate-800 rounded-sm overflow-hidden mb-6">
<div class="flex items-center justify-between px-4 py-2 border-b border-slate-700">
<span class="text-sm text-slate-400">HTTP Header</span>
<div class="bg-zinc-800 rounded-sm overflow-hidden mb-6">
<div class="flex items-center justify-between px-4 py-2 border-b border-zinc-700">
<span class="text-sm text-zinc-400">HTTP Header</span>
</div>
<pre class="overflow-x-auto p-4 text-sm"><code class="font-pt-mono text-slate-300">Authorization: Bearer hk_your_api_key_here</code></pre>
<pre class="overflow-x-auto p-4 text-sm"><code class="font-pt-mono text-zinc-300">Authorization: Bearer hk_your_api_key_here</code></pre>
</div>
<p class="text-slate-600 dark:text-slate-400 mb-4">
<p class="text-zinc-600 dark:text-zinc-400 mb-4">
Example request with cURL:
</p>
<div class="bg-slate-800 rounded-sm overflow-hidden">
<div class="flex items-center justify-between px-4 py-2 border-b border-slate-700">
<span class="text-sm text-slate-400">cURL</span>
<div class="bg-zinc-800 rounded-sm overflow-hidden">
<div class="flex items-center justify-between px-4 py-2 border-b border-zinc-700">
<span class="text-sm text-zinc-400">cURL</span>
</div>
<pre class="overflow-x-auto p-4 text-sm"><code class="font-pt-mono text-slate-300"><span class="text-teal-400">curl</span> <span class="text-slate-500">--request</span> GET \
<span class="text-slate-500">--url</span> <span class="text-amber-400">'https://api.host.uk.com/api/v1/bio'</span> \
<span class="text-slate-500">--header</span> <span class="text-amber-400">'Authorization: Bearer hk_your_api_key'</span></code></pre>
<pre class="overflow-x-auto p-4 text-sm"><code class="font-pt-mono text-zinc-300"><span class="text-teal-400">curl</span> <span class="text-zinc-500">--request</span> GET \
<span class="text-zinc-500">--url</span> <span class="text-amber-400">'https://api.lthn.ai/v1/brain/recall'</span> \
<span class="text-zinc-500">--header</span> <span class="text-amber-400">'Authorization: Bearer hk_your_api_key'</span></code></pre>
</div>
</section>
{{-- Scopes --}}
<section id="scopes" data-scrollspy-target class="mb-12">
<h2 class="h3 mb-4 text-slate-800 dark:text-slate-100">Scopes</h2>
<p class="text-slate-600 dark:text-slate-400 mb-4">
<h2 class="h3 mb-4 text-zinc-800 dark:text-zinc-100">Scopes</h2>
<p class="text-zinc-600 dark:text-zinc-400 mb-4">
API keys can have different scopes to limit their permissions:
</p>
<div class="overflow-x-auto">
<table class="w-full text-sm">
<thead>
<tr class="border-b border-slate-200 dark:border-slate-700">
<th class="text-left py-3 px-4 font-medium text-slate-800 dark:text-slate-200">Scope</th>
<th class="text-left py-3 px-4 font-medium text-slate-800 dark:text-slate-200">Description</th>
<tr class="border-b border-zinc-200 dark:border-zinc-700">
<th class="text-left py-3 px-4 font-medium text-zinc-800 dark:text-zinc-200">Scope</th>
<th class="text-left py-3 px-4 font-medium text-zinc-800 dark:text-zinc-200">Description</th>
</tr>
</thead>
<tbody class="divide-y divide-slate-200 dark:divide-slate-700">
<tbody class="divide-y divide-zinc-200 dark:divide-zinc-700">
<tr>
<td class="py-3 px-4"><code class="px-1.5 py-0.5 bg-slate-100 dark:bg-slate-800 rounded text-xs">read</code></td>
<td class="py-3 px-4 text-slate-600 dark:text-slate-400">Read access to resources (GET requests)</td>
<td class="py-3 px-4"><code class="px-1.5 py-0.5 bg-zinc-100 dark:bg-zinc-800 rounded text-xs">read</code></td>
<td class="py-3 px-4 text-zinc-600 dark:text-zinc-400">Read access to resources (GET requests)</td>
</tr>
<tr>
<td class="py-3 px-4"><code class="px-1.5 py-0.5 bg-slate-100 dark:bg-slate-800 rounded text-xs">write</code></td>
<td class="py-3 px-4 text-slate-600 dark:text-slate-400">Create and update resources (POST, PUT requests)</td>
<td class="py-3 px-4"><code class="px-1.5 py-0.5 bg-zinc-100 dark:bg-zinc-800 rounded text-xs">write</code></td>
<td class="py-3 px-4 text-zinc-600 dark:text-zinc-400">Create and update resources (POST, PUT requests)</td>
</tr>
<tr>
<td class="py-3 px-4"><code class="px-1.5 py-0.5 bg-slate-100 dark:bg-slate-800 rounded text-xs">delete</code></td>
<td class="py-3 px-4 text-slate-600 dark:text-slate-400">Delete resources (DELETE requests)</td>
<td class="py-3 px-4"><code class="px-1.5 py-0.5 bg-zinc-100 dark:bg-zinc-800 rounded text-xs">delete</code></td>
<td class="py-3 px-4 text-zinc-600 dark:text-zinc-400">Delete resources (DELETE requests)</td>
</tr>
</tbody>
</table>
@ -159,8 +159,8 @@
{{-- Security --}}
<section id="security" data-scrollspy-target class="mb-12">
<h2 class="h3 mb-4 text-slate-800 dark:text-slate-100">Security Best Practices</h2>
<ul class="list-disc list-inside space-y-2 text-slate-600 dark:text-slate-400">
<h2 class="h3 mb-4 text-zinc-800 dark:text-zinc-100">Security Best Practices</h2>
<ul class="list-disc list-inside space-y-2 text-zinc-600 dark:text-zinc-400">
<li>Never commit API keys to version control</li>
<li>Use environment variables to store keys</li>
<li>Rotate keys periodically</li>
@ -171,12 +171,12 @@
</section>
{{-- Next steps --}}
<div class="flex items-center justify-between pt-8 border-t border-slate-200 dark:border-slate-700">
<a href="{{ route('api.guides.quickstart') }}" class="text-slate-600 hover:text-slate-900 dark:text-slate-400 dark:hover:text-slate-200">
<div class="flex items-center justify-between pt-8 border-t border-zinc-200 dark:border-zinc-700">
<a href="{{ route('api.guides.quickstart') }}" class="text-zinc-600 hover:text-zinc-900 dark:text-zinc-400 dark:hover:text-zinc-200">
&larr; Quick Start
</a>
<a href="{{ route('api.guides.biolinks') }}" class="text-blue-600 hover:text-blue-700 dark:hover:text-blue-500 font-medium">
Managing Biolinks &rarr;
<a href="{{ route('api.guides.webhooks') }}" class="text-cyan-600 hover:text-cyan-700 dark:hover:text-cyan-500 font-medium">
Webhooks &rarr;
</a>
</div>

View file

@ -6,32 +6,32 @@
<div class="flex">
{{-- Sidebar --}}
<aside class="hidden lg:block fixed left-0 top-16 md:top-20 bottom-0 w-64 border-r border-slate-200 dark:border-slate-800">
<aside class="hidden lg:block fixed left-0 top-16 md:top-20 bottom-0 w-64 border-r border-zinc-200 dark:border-zinc-800">
<div class="h-full px-4 py-8 overflow-y-auto no-scrollbar">
<nav>
<ul class="space-y-2">
<li>
<a href="#overview" data-scrollspy-link class="block px-3 py-2 text-sm text-slate-600 hover:text-slate-900 dark:text-slate-400 dark:hover:text-slate-200 rounded-sm relative before:absolute before:inset-y-1 before:left-0 before:w-0.5 before:rounded-full">
<a href="#overview" data-scrollspy-link class="block px-3 py-2 text-sm text-zinc-600 hover:text-zinc-900 dark:text-zinc-400 dark:hover:text-zinc-200 rounded-sm relative before:absolute before:inset-y-1 before:left-0 before:w-0.5 before:rounded-full">
Overview
</a>
</li>
<li>
<a href="#http-codes" data-scrollspy-link class="block px-3 py-2 text-sm text-slate-600 hover:text-slate-900 dark:text-slate-400 dark:hover:text-slate-200 rounded-sm relative before:absolute before:inset-y-1 before:left-0 before:w-0.5 before:rounded-full">
<a href="#http-codes" data-scrollspy-link class="block px-3 py-2 text-sm text-zinc-600 hover:text-zinc-900 dark:text-zinc-400 dark:hover:text-zinc-200 rounded-sm relative before:absolute before:inset-y-1 before:left-0 before:w-0.5 before:rounded-full">
HTTP Status Codes
</a>
</li>
<li>
<a href="#error-format" data-scrollspy-link class="block px-3 py-2 text-sm text-slate-600 hover:text-slate-900 dark:text-slate-400 dark:hover:text-slate-200 rounded-sm relative before:absolute before:inset-y-1 before:left-0 before:w-0.5 before:rounded-full">
<a href="#error-format" data-scrollspy-link class="block px-3 py-2 text-sm text-zinc-600 hover:text-zinc-900 dark:text-zinc-400 dark:hover:text-zinc-200 rounded-sm relative before:absolute before:inset-y-1 before:left-0 before:w-0.5 before:rounded-full">
Error Format
</a>
</li>
<li>
<a href="#common-errors" data-scrollspy-link class="block px-3 py-2 text-sm text-slate-600 hover:text-slate-900 dark:text-slate-400 dark:hover:text-slate-200 rounded-sm relative before:absolute before:inset-y-1 before:left-0 before:w-0.5 before:rounded-full">
<a href="#common-errors" data-scrollspy-link class="block px-3 py-2 text-sm text-zinc-600 hover:text-zinc-900 dark:text-zinc-400 dark:hover:text-zinc-200 rounded-sm relative before:absolute before:inset-y-1 before:left-0 before:w-0.5 before:rounded-full">
Common Errors
</a>
</li>
<li>
<a href="#rate-limiting" data-scrollspy-link class="block px-3 py-2 text-sm text-slate-600 hover:text-slate-900 dark:text-slate-400 dark:hover:text-slate-200 rounded-sm relative before:absolute before:inset-y-1 before:left-0 before:w-0.5 before:rounded-full">
<a href="#rate-limiting" data-scrollspy-link class="block px-3 py-2 text-sm text-zinc-600 hover:text-zinc-900 dark:text-zinc-400 dark:hover:text-zinc-200 rounded-sm relative before:absolute before:inset-y-1 before:left-0 before:w-0.5 before:rounded-full">
Rate Limiting
</a>
</li>
@ -47,73 +47,73 @@
{{-- Breadcrumb --}}
<nav class="mb-8">
<ol class="flex items-center gap-2 text-sm">
<li><a href="{{ route('api.guides') }}" class="text-slate-500 hover:text-slate-700 dark:text-slate-400 dark:hover:text-slate-200">Guides</a></li>
<li class="text-slate-400">/</li>
<li class="text-slate-800 dark:text-slate-200">Error Handling</li>
<li><a href="{{ route('api.guides') }}" class="text-zinc-500 hover:text-zinc-700 dark:text-zinc-400 dark:hover:text-zinc-200">Guides</a></li>
<li class="text-zinc-400">/</li>
<li class="text-zinc-800 dark:text-zinc-200">Error Handling</li>
</ol>
</nav>
<h1 class="h1 mb-4 text-slate-800 dark:text-slate-100">Error Handling</h1>
<p class="text-xl text-slate-600 dark:text-slate-400 mb-12">
<h1 class="h1 mb-4 text-zinc-800 dark:text-zinc-100">Error Handling</h1>
<p class="text-xl text-zinc-600 dark:text-zinc-400 mb-12">
Understand API error codes and how to handle them gracefully.
</p>
{{-- Overview --}}
<section id="overview" data-scrollspy-target class="mb-12">
<h2 class="h3 mb-4 text-slate-800 dark:text-slate-100">Overview</h2>
<p class="text-slate-600 dark:text-slate-400 mb-4">
<h2 class="h3 mb-4 text-zinc-800 dark:text-zinc-100">Overview</h2>
<p class="text-zinc-600 dark:text-zinc-400 mb-4">
The API uses conventional HTTP response codes to indicate success or failure. Codes in the 2xx range indicate success, 4xx indicate client errors, and 5xx indicate server errors.
</p>
</section>
{{-- HTTP Codes --}}
<section id="http-codes" data-scrollspy-target class="mb-12">
<h2 class="h3 mb-4 text-slate-800 dark:text-slate-100">HTTP Status Codes</h2>
<h2 class="h3 mb-4 text-zinc-800 dark:text-zinc-100">HTTP Status Codes</h2>
<div class="overflow-x-auto">
<table class="w-full text-sm">
<thead>
<tr class="border-b border-slate-200 dark:border-slate-700">
<th class="text-left py-3 px-4 font-medium text-slate-800 dark:text-slate-200">Code</th>
<th class="text-left py-3 px-4 font-medium text-slate-800 dark:text-slate-200">Meaning</th>
<tr class="border-b border-zinc-200 dark:border-zinc-700">
<th class="text-left py-3 px-4 font-medium text-zinc-800 dark:text-zinc-200">Code</th>
<th class="text-left py-3 px-4 font-medium text-zinc-800 dark:text-zinc-200">Meaning</th>
</tr>
</thead>
<tbody class="divide-y divide-slate-200 dark:divide-slate-700">
<tbody class="divide-y divide-zinc-200 dark:divide-zinc-700">
<tr>
<td class="py-3 px-4"><span class="px-2 py-1 bg-green-100 text-green-700 dark:bg-green-900/30 dark:text-green-400 rounded text-xs font-medium">200</span></td>
<td class="py-3 px-4 text-slate-600 dark:text-slate-400">Success - Request completed successfully</td>
<td class="py-3 px-4 text-zinc-600 dark:text-zinc-400">Success - Request completed successfully</td>
</tr>
<tr>
<td class="py-3 px-4"><span class="px-2 py-1 bg-green-100 text-green-700 dark:bg-green-900/30 dark:text-green-400 rounded text-xs font-medium">201</span></td>
<td class="py-3 px-4 text-slate-600 dark:text-slate-400">Created - Resource was created successfully</td>
<td class="py-3 px-4 text-zinc-600 dark:text-zinc-400">Created - Resource was created successfully</td>
</tr>
<tr>
<td class="py-3 px-4"><span class="px-2 py-1 bg-amber-100 text-amber-700 dark:bg-amber-900/30 dark:text-amber-400 rounded text-xs font-medium">400</span></td>
<td class="py-3 px-4 text-slate-600 dark:text-slate-400">Bad Request - Invalid request parameters</td>
<td class="py-3 px-4 text-zinc-600 dark:text-zinc-400">Bad Request - Invalid request parameters</td>
</tr>
<tr>
<td class="py-3 px-4"><span class="px-2 py-1 bg-amber-100 text-amber-700 dark:bg-amber-900/30 dark:text-amber-400 rounded text-xs font-medium">401</span></td>
<td class="py-3 px-4 text-slate-600 dark:text-slate-400">Unauthorised - Invalid or missing API key</td>
<td class="py-3 px-4 text-zinc-600 dark:text-zinc-400">Unauthorised - Invalid or missing API key</td>
</tr>
<tr>
<td class="py-3 px-4"><span class="px-2 py-1 bg-amber-100 text-amber-700 dark:bg-amber-900/30 dark:text-amber-400 rounded text-xs font-medium">403</span></td>
<td class="py-3 px-4 text-slate-600 dark:text-slate-400">Forbidden - Insufficient permissions</td>
<td class="py-3 px-4 text-zinc-600 dark:text-zinc-400">Forbidden - Insufficient permissions</td>
</tr>
<tr>
<td class="py-3 px-4"><span class="px-2 py-1 bg-amber-100 text-amber-700 dark:bg-amber-900/30 dark:text-amber-400 rounded text-xs font-medium">404</span></td>
<td class="py-3 px-4 text-slate-600 dark:text-slate-400">Not Found - Resource doesn't exist</td>
<td class="py-3 px-4 text-zinc-600 dark:text-zinc-400">Not Found - Resource doesn't exist</td>
</tr>
<tr>
<td class="py-3 px-4"><span class="px-2 py-1 bg-amber-100 text-amber-700 dark:bg-amber-900/30 dark:text-amber-400 rounded text-xs font-medium">422</span></td>
<td class="py-3 px-4 text-slate-600 dark:text-slate-400">Unprocessable - Validation failed</td>
<td class="py-3 px-4 text-zinc-600 dark:text-zinc-400">Unprocessable - Validation failed</td>
</tr>
<tr>
<td class="py-3 px-4"><span class="px-2 py-1 bg-amber-100 text-amber-700 dark:bg-amber-900/30 dark:text-amber-400 rounded text-xs font-medium">429</span></td>
<td class="py-3 px-4 text-slate-600 dark:text-slate-400">Too Many Requests - Rate limit exceeded</td>
<td class="py-3 px-4 text-zinc-600 dark:text-zinc-400">Too Many Requests - Rate limit exceeded</td>
</tr>
<tr>
<td class="py-3 px-4"><span class="px-2 py-1 bg-red-100 text-red-700 dark:bg-red-900/30 dark:text-red-400 rounded text-xs font-medium">500</span></td>
<td class="py-3 px-4 text-slate-600 dark:text-slate-400">Server Error - Something went wrong on our end</td>
<td class="py-3 px-4 text-zinc-600 dark:text-zinc-400">Server Error - Something went wrong on our end</td>
</tr>
</tbody>
</table>
@ -122,13 +122,13 @@
{{-- Error Format --}}
<section id="error-format" data-scrollspy-target class="mb-12">
<h2 class="h3 mb-4 text-slate-800 dark:text-slate-100">Error Format</h2>
<p class="text-slate-600 dark:text-slate-400 mb-4">
<h2 class="h3 mb-4 text-zinc-800 dark:text-zinc-100">Error Format</h2>
<p class="text-zinc-600 dark:text-zinc-400 mb-4">
Error responses include a JSON body with details:
</p>
<div class="bg-slate-800 rounded-sm overflow-hidden">
<pre class="overflow-x-auto p-4 text-sm"><code class="font-pt-mono text-slate-300">{
<div class="bg-zinc-800 rounded-sm overflow-hidden">
<pre class="overflow-x-auto p-4 text-sm"><code class="font-pt-mono text-zinc-300">{
<span class="text-blue-400">"message"</span>: <span class="text-green-400">"The given data was invalid."</span>,
<span class="text-blue-400">"errors"</span>: {
<span class="text-blue-400">"url"</span>: [
@ -141,65 +141,65 @@
{{-- Common Errors --}}
<section id="common-errors" data-scrollspy-target class="mb-12">
<h2 class="h3 mb-4 text-slate-800 dark:text-slate-100">Common Errors</h2>
<h2 class="h3 mb-4 text-zinc-800 dark:text-zinc-100">Common Errors</h2>
<div class="space-y-4">
<div class="p-4 border border-slate-200 dark:border-slate-700 rounded-sm">
<h4 class="font-medium text-slate-800 dark:text-slate-200 mb-2">Invalid API Key</h4>
<p class="text-sm text-slate-600 dark:text-slate-400 mb-2">
<div class="p-4 border border-zinc-200 dark:border-zinc-700 rounded-sm">
<h4 class="font-medium text-zinc-800 dark:text-zinc-200 mb-2">Invalid API Key</h4>
<p class="text-sm text-zinc-600 dark:text-zinc-400 mb-2">
Returned when the API key is missing, malformed, or revoked.
</p>
<code class="text-xs px-2 py-1 bg-slate-100 dark:bg-slate-800 rounded">401 Unauthorised</code>
<code class="text-xs px-2 py-1 bg-zinc-100 dark:bg-zinc-800 rounded">401 Unauthorised</code>
</div>
<div class="p-4 border border-slate-200 dark:border-slate-700 rounded-sm">
<h4 class="font-medium text-slate-800 dark:text-slate-200 mb-2">Resource Not Found</h4>
<p class="text-sm text-slate-600 dark:text-slate-400 mb-2">
<div class="p-4 border border-zinc-200 dark:border-zinc-700 rounded-sm">
<h4 class="font-medium text-zinc-800 dark:text-zinc-200 mb-2">Resource Not Found</h4>
<p class="text-sm text-zinc-600 dark:text-zinc-400 mb-2">
The requested resource (biolink, workspace, etc.) doesn't exist or you don't have access.
</p>
<code class="text-xs px-2 py-1 bg-slate-100 dark:bg-slate-800 rounded">404 Not Found</code>
<code class="text-xs px-2 py-1 bg-zinc-100 dark:bg-zinc-800 rounded">404 Not Found</code>
</div>
<div class="p-4 border border-slate-200 dark:border-slate-700 rounded-sm">
<h4 class="font-medium text-slate-800 dark:text-slate-200 mb-2">Validation Failed</h4>
<p class="text-sm text-slate-600 dark:text-slate-400 mb-2">
<div class="p-4 border border-zinc-200 dark:border-zinc-700 rounded-sm">
<h4 class="font-medium text-zinc-800 dark:text-zinc-200 mb-2">Validation Failed</h4>
<p class="text-sm text-zinc-600 dark:text-zinc-400 mb-2">
Request data failed validation. Check the <code>errors</code> object for specific fields.
</p>
<code class="text-xs px-2 py-1 bg-slate-100 dark:bg-slate-800 rounded">422 Unprocessable Entity</code>
<code class="text-xs px-2 py-1 bg-zinc-100 dark:bg-zinc-800 rounded">422 Unprocessable Entity</code>
</div>
</div>
</section>
{{-- Rate Limiting --}}
<section id="rate-limiting" data-scrollspy-target class="mb-12">
<h2 class="h3 mb-4 text-slate-800 dark:text-slate-100">Rate Limiting</h2>
<p class="text-slate-600 dark:text-slate-400 mb-4">
<h2 class="h3 mb-4 text-zinc-800 dark:text-zinc-100">Rate Limiting</h2>
<p class="text-zinc-600 dark:text-zinc-400 mb-4">
API requests are rate limited to ensure fair usage. Rate limit headers are included in all responses:
</p>
<div class="bg-slate-800 rounded-sm overflow-hidden mb-4">
<pre class="overflow-x-auto p-4 text-sm"><code class="font-pt-mono text-slate-300">X-RateLimit-Limit: 60
<div class="bg-zinc-800 rounded-sm overflow-hidden mb-4">
<pre class="overflow-x-auto p-4 text-sm"><code class="font-pt-mono text-zinc-300">X-RateLimit-Limit: 60
X-RateLimit-Remaining: 58
X-RateLimit-Reset: 1705320000</code></pre>
</div>
<p class="text-slate-600 dark:text-slate-400 mb-4">
<p class="text-zinc-600 dark:text-zinc-400 mb-4">
When rate limited, you'll receive a 429 response. Wait until the reset timestamp before retrying.
</p>
<div class="text-sm p-4 bg-slate-50 border border-slate-200 rounded-sm dark:bg-slate-800 dark:border-slate-700">
<p class="text-slate-600 dark:text-slate-400">
<div class="text-sm p-4 bg-zinc-50 border border-zinc-200 rounded-sm dark:bg-zinc-800 dark:border-zinc-700">
<p class="text-zinc-600 dark:text-zinc-400">
<strong>Tip:</strong> Implement exponential backoff in your retry logic. Start with a 1-second delay and double it with each retry, up to a maximum of 32 seconds.
</p>
</div>
</section>
{{-- Next steps --}}
<div class="flex items-center justify-between pt-8 border-t border-slate-200 dark:border-slate-700">
<a href="{{ route('api.guides.webhooks') }}" class="text-slate-600 hover:text-slate-900 dark:text-slate-400 dark:hover:text-slate-200">
<div class="flex items-center justify-between pt-8 border-t border-zinc-200 dark:border-zinc-700">
<a href="{{ route('api.guides.webhooks') }}" class="text-zinc-600 hover:text-zinc-900 dark:text-zinc-400 dark:hover:text-zinc-200">
&larr; Webhooks
</a>
<a href="{{ route('api.reference') }}" class="text-blue-600 hover:text-blue-700 dark:hover:text-blue-500 font-medium">
<a href="{{ route('api.reference') }}" class="text-cyan-600 hover:text-cyan-700 dark:hover:text-cyan-500 font-medium">
API Reference &rarr;
</a>
</div>

View file

@ -5,8 +5,8 @@
@section('content')
<div class="max-w-7xl mx-auto px-4 sm:px-6 py-12">
<div class="max-w-3xl">
<h1 class="h2 mb-4 text-slate-800 dark:text-slate-100">Guides</h1>
<p class="text-lg text-slate-600 dark:text-slate-400 mb-12">
<h1 class="h2 mb-4 text-zinc-800 dark:text-zinc-100">Guides</h1>
<p class="text-lg text-zinc-600 dark:text-zinc-400 mb-12">
Step-by-step tutorials and best practices for integrating with the API.
</p>
</div>
@ -14,73 +14,51 @@
<div class="grid md:grid-cols-2 lg:grid-cols-3 gap-6">
{{-- Quick Start --}}
<a href="{{ route('api.guides.quickstart') }}" class="group block p-6 bg-white dark:bg-slate-800 border border-slate-200 dark:border-slate-700 rounded-sm hover:border-blue-300 dark:hover:border-blue-600 transition-colors">
<a href="{{ route('api.guides.quickstart') }}" class="group block p-6 bg-white dark:bg-zinc-800 border border-zinc-200 dark:border-zinc-700 rounded-sm hover:border-cyan-300 dark:hover:border-cyan-600 transition-colors">
<div class="flex items-center gap-3 mb-3">
<div class="w-8 h-8 flex items-center justify-center bg-blue-100 dark:bg-blue-900/30 rounded-sm">
<svg class="w-4 h-4 fill-blue-600" viewBox="0 0 16 16">
<path d="M11.953 4.29a.5.5 0 0 0-.454-.292H6.14L6.984.62A.5.5 0 0 0 6.12.173l-6 7a.5.5 0 0 0 .379.825h5.359l-.844 3.38a.5.5 0 0 0 .864.445l6-7a.5.5 0 0 0 .075-.534Z" />
</svg>
<div class="w-8 h-8 flex items-center justify-center bg-cyan-100 dark:bg-cyan-900/30 rounded-sm">
<i class="fa-solid fa-bolt text-cyan-600 text-sm"></i>
</div>
<span class="text-xs font-medium text-slate-500 dark:text-slate-400 uppercase tracking-wider">Getting Started</span>
<span class="text-xs font-medium text-zinc-500 dark:text-zinc-400 uppercase tracking-wider">Getting Started</span>
</div>
<h3 class="h4 mb-2 text-slate-800 dark:text-slate-100 group-hover:text-blue-600 dark:group-hover:text-blue-500">Quick Start</h3>
<p class="text-sm text-slate-600 dark:text-slate-400">Get up and running with the API in under 5 minutes.</p>
<h3 class="h4 mb-2 text-zinc-800 dark:text-zinc-100 group-hover:text-cyan-600 dark:group-hover:text-cyan-500">Quick Start</h3>
<p class="text-sm text-zinc-600 dark:text-zinc-400">Get up and running with the API in under 5 minutes.</p>
</a>
{{-- Authentication --}}
<a href="{{ route('api.guides.authentication') }}" class="group block p-6 bg-white dark:bg-slate-800 border border-slate-200 dark:border-slate-700 rounded-sm hover:border-blue-300 dark:hover:border-blue-600 transition-colors">
<a href="{{ route('api.guides.authentication') }}" class="group block p-6 bg-white dark:bg-zinc-800 border border-zinc-200 dark:border-zinc-700 rounded-sm hover:border-cyan-300 dark:hover:border-cyan-600 transition-colors">
<div class="flex items-center gap-3 mb-3">
<div class="w-8 h-8 flex items-center justify-center bg-amber-100 dark:bg-amber-900/30 rounded-sm">
<svg class="w-4 h-4 fill-amber-600" viewBox="0 0 16 16">
<path d="M8 1a4 4 0 0 0-4 4v3H3a1 1 0 0 0-1 1v5a1 1 0 0 0 1 1h10a1 1 0 0 0 1-1V9a1 1 0 0 0-1-1h-1V5a4 4 0 0 0-4-4zm2 7V5a2 2 0 1 0-4 0v3h4z"/>
</svg>
<i class="fa-solid fa-key text-amber-600 text-sm"></i>
</div>
<span class="text-xs font-medium text-slate-500 dark:text-slate-400 uppercase tracking-wider">Security</span>
<span class="text-xs font-medium text-zinc-500 dark:text-zinc-400 uppercase tracking-wider">Security</span>
</div>
<h3 class="h4 mb-2 text-slate-800 dark:text-slate-100 group-hover:text-blue-600 dark:group-hover:text-blue-500">Authentication</h3>
<p class="text-sm text-slate-600 dark:text-slate-400">Learn how to authenticate your API requests using API keys.</p>
</a>
{{-- QR Codes --}}
<a href="{{ route('api.guides.qrcodes') }}" class="group block p-6 bg-white dark:bg-slate-800 border border-slate-200 dark:border-slate-700 rounded-sm hover:border-blue-300 dark:hover:border-blue-600 transition-colors">
<div class="flex items-center gap-3 mb-3">
<div class="w-8 h-8 flex items-center justify-center bg-teal-100 dark:bg-teal-900/30 rounded-sm">
<svg class="w-4 h-4 fill-teal-600" viewBox="0 0 16 16">
<path d="M2 3a1 1 0 0 1 1-1h3a1 1 0 0 1 1 1v3a1 1 0 0 1-1 1H3a1 1 0 0 1-1-1V3zm2 2V4h1v1H4z"/>
</svg>
</div>
<span class="text-xs font-medium text-slate-500 dark:text-slate-400 uppercase tracking-wider">Core</span>
</div>
<h3 class="h4 mb-2 text-slate-800 dark:text-slate-100 group-hover:text-blue-600 dark:group-hover:text-blue-500">QR Code Generation</h3>
<p class="text-sm text-slate-600 dark:text-slate-400">Generate customisable QR codes with colours, logos, and formats.</p>
<h3 class="h4 mb-2 text-zinc-800 dark:text-zinc-100 group-hover:text-cyan-600 dark:group-hover:text-cyan-500">Authentication</h3>
<p class="text-sm text-zinc-600 dark:text-zinc-400">Learn how to authenticate your API requests using API keys.</p>
</a>
{{-- Webhooks --}}
<a href="{{ route('api.guides.webhooks') }}" class="group block p-6 bg-white dark:bg-slate-800 border border-slate-200 dark:border-slate-700 rounded-sm hover:border-blue-300 dark:hover:border-blue-600 transition-colors">
<a href="{{ route('api.guides.webhooks') }}" class="group block p-6 bg-white dark:bg-zinc-800 border border-zinc-200 dark:border-zinc-700 rounded-sm hover:border-cyan-300 dark:hover:border-cyan-600 transition-colors">
<div class="flex items-center gap-3 mb-3">
<div class="w-8 h-8 flex items-center justify-center bg-rose-100 dark:bg-rose-900/30 rounded-sm">
<svg class="w-4 h-4 fill-rose-600" viewBox="0 0 16 16">
<path d="M8.94 1.5a.75.75 0 0 0-1.06 0L7 2.38 6.12 1.5a.75.75 0 0 0-1.06 1.06l.88.88-.88.88a.75.75 0 1 0 1.06 1.06L7 4.5l.88.88a.75.75 0 1 0 1.06-1.06l-.88-.88.88-.88a.75.75 0 0 0 0-1.06z"/>
</svg>
<i class="fa-solid fa-satellite-dish text-rose-600 text-sm"></i>
</div>
<span class="text-xs font-medium text-slate-500 dark:text-slate-400 uppercase tracking-wider">Advanced</span>
<span class="text-xs font-medium text-zinc-500 dark:text-zinc-400 uppercase tracking-wider">Advanced</span>
</div>
<h3 class="h4 mb-2 text-slate-800 dark:text-slate-100 group-hover:text-blue-600 dark:group-hover:text-blue-500">Webhooks</h3>
<p class="text-sm text-slate-600 dark:text-slate-400">Receive real-time notifications for events in your workspace.</p>
<h3 class="h4 mb-2 text-zinc-800 dark:text-zinc-100 group-hover:text-cyan-600 dark:group-hover:text-cyan-500">Webhooks</h3>
<p class="text-sm text-zinc-600 dark:text-zinc-400">Receive real-time notifications for events in your workspace.</p>
</a>
{{-- Error Handling --}}
<a href="{{ route('api.guides.errors') }}" class="group block p-6 bg-white dark:bg-slate-800 border border-slate-200 dark:border-slate-700 rounded-sm hover:border-blue-300 dark:hover:border-blue-600 transition-colors">
<a href="{{ route('api.guides.errors') }}" class="group block p-6 bg-white dark:bg-zinc-800 border border-zinc-200 dark:border-zinc-700 rounded-sm hover:border-cyan-300 dark:hover:border-cyan-600 transition-colors">
<div class="flex items-center gap-3 mb-3">
<div class="w-8 h-8 flex items-center justify-center bg-slate-100 dark:bg-slate-700 rounded-sm">
<svg class="w-4 h-4 fill-slate-600 dark:fill-slate-400" viewBox="0 0 16 16">
<path d="M8 1a7 7 0 1 0 0 14A7 7 0 0 0 8 1zm0 3a.75.75 0 0 1 .75.75v3.5a.75.75 0 0 1-1.5 0v-3.5A.75.75 0 0 1 8 4zm0 8a1 1 0 1 1 0-2 1 1 0 0 1 0 2z"/>
</svg>
<div class="w-8 h-8 flex items-center justify-center bg-zinc-100 dark:bg-zinc-700 rounded-sm">
<i class="fa-solid fa-triangle-exclamation text-zinc-600 dark:text-zinc-400 text-sm"></i>
</div>
<span class="text-xs font-medium text-slate-500 dark:text-slate-400 uppercase tracking-wider">Reference</span>
<span class="text-xs font-medium text-zinc-500 dark:text-zinc-400 uppercase tracking-wider">Reference</span>
</div>
<h3 class="h4 mb-2 text-slate-800 dark:text-slate-100 group-hover:text-blue-600 dark:group-hover:text-blue-500">Error Handling</h3>
<p class="text-sm text-slate-600 dark:text-slate-400">Understand API error codes and how to handle them gracefully.</p>
<h3 class="h4 mb-2 text-zinc-800 dark:text-zinc-100 group-hover:text-cyan-600 dark:group-hover:text-cyan-500">Error Handling</h3>
<p class="text-sm text-zinc-600 dark:text-zinc-400">Understand API error codes and how to handle them gracefully.</p>
</a>
</div>

View file

@ -6,32 +6,32 @@
<div class="flex">
{{-- Sidebar --}}
<aside class="hidden lg:block fixed left-0 top-16 md:top-20 bottom-0 w-64 border-r border-slate-200 dark:border-slate-800">
<aside class="hidden lg:block fixed left-0 top-16 md:top-20 bottom-0 w-64 border-r border-zinc-200 dark:border-zinc-800">
<div class="h-full px-4 py-8 overflow-y-auto no-scrollbar">
<nav>
<ul class="space-y-2">
<li>
<a href="#overview" data-scrollspy-link class="block px-3 py-2 text-sm text-slate-600 hover:text-slate-900 dark:text-slate-400 dark:hover:text-slate-200 rounded-sm relative before:absolute before:inset-y-1 before:left-0 before:w-0.5 before:rounded-full">
<a href="#overview" data-scrollspy-link class="block px-3 py-2 text-sm text-zinc-600 hover:text-zinc-900 dark:text-zinc-400 dark:hover:text-zinc-200 rounded-sm relative before:absolute before:inset-y-1 before:left-0 before:w-0.5 before:rounded-full">
Overview
</a>
</li>
<li>
<a href="#biolink-qr" data-scrollspy-link class="block px-3 py-2 text-sm text-slate-600 hover:text-slate-900 dark:text-slate-400 dark:hover:text-slate-200 rounded-sm relative before:absolute before:inset-y-1 before:left-0 before:w-0.5 before:rounded-full">
<a href="#biolink-qr" data-scrollspy-link class="block px-3 py-2 text-sm text-zinc-600 hover:text-zinc-900 dark:text-zinc-400 dark:hover:text-zinc-200 rounded-sm relative before:absolute before:inset-y-1 before:left-0 before:w-0.5 before:rounded-full">
Biolink QR Codes
</a>
</li>
<li>
<a href="#custom-qr" data-scrollspy-link class="block px-3 py-2 text-sm text-slate-600 hover:text-slate-900 dark:text-slate-400 dark:hover:text-slate-200 rounded-sm relative before:absolute before:inset-y-1 before:left-0 before:w-0.5 before:rounded-full">
<a href="#custom-qr" data-scrollspy-link class="block px-3 py-2 text-sm text-zinc-600 hover:text-zinc-900 dark:text-zinc-400 dark:hover:text-zinc-200 rounded-sm relative before:absolute before:inset-y-1 before:left-0 before:w-0.5 before:rounded-full">
Custom URL QR Codes
</a>
</li>
<li>
<a href="#options" data-scrollspy-link class="block px-3 py-2 text-sm text-slate-600 hover:text-slate-900 dark:text-slate-400 dark:hover:text-slate-200 rounded-sm relative before:absolute before:inset-y-1 before:left-0 before:w-0.5 before:rounded-full">
<a href="#options" data-scrollspy-link class="block px-3 py-2 text-sm text-zinc-600 hover:text-zinc-900 dark:text-zinc-400 dark:hover:text-zinc-200 rounded-sm relative before:absolute before:inset-y-1 before:left-0 before:w-0.5 before:rounded-full">
Customisation Options
</a>
</li>
<li>
<a href="#download" data-scrollspy-link class="block px-3 py-2 text-sm text-slate-600 hover:text-slate-900 dark:text-slate-400 dark:hover:text-slate-200 rounded-sm relative before:absolute before:inset-y-1 before:left-0 before:w-0.5 before:rounded-full">
<a href="#download" data-scrollspy-link class="block px-3 py-2 text-sm text-zinc-600 hover:text-zinc-900 dark:text-zinc-400 dark:hover:text-zinc-200 rounded-sm relative before:absolute before:inset-y-1 before:left-0 before:w-0.5 before:rounded-full">
Download Formats
</a>
</li>
@ -47,24 +47,24 @@
{{-- Breadcrumb --}}
<nav class="mb-8">
<ol class="flex items-center gap-2 text-sm">
<li><a href="{{ route('api.guides') }}" class="text-slate-500 hover:text-slate-700 dark:text-slate-400 dark:hover:text-slate-200">Guides</a></li>
<li class="text-slate-400">/</li>
<li class="text-slate-800 dark:text-slate-200">QR Code Generation</li>
<li><a href="{{ route('api.guides') }}" class="text-zinc-500 hover:text-zinc-700 dark:text-zinc-400 dark:hover:text-zinc-200">Guides</a></li>
<li class="text-zinc-400">/</li>
<li class="text-zinc-800 dark:text-zinc-200">QR Code Generation</li>
</ol>
</nav>
<h1 class="h1 mb-4 text-slate-800 dark:text-slate-100">QR Code Generation</h1>
<p class="text-xl text-slate-600 dark:text-slate-400 mb-12">
<h1 class="h1 mb-4 text-zinc-800 dark:text-zinc-100">QR Code Generation</h1>
<p class="text-xl text-zinc-600 dark:text-zinc-400 mb-12">
Generate customisable QR codes for your biolinks or any URL.
</p>
{{-- Overview --}}
<section id="overview" data-scrollspy-target class="mb-12">
<h2 class="h3 mb-4 text-slate-800 dark:text-slate-100">Overview</h2>
<p class="text-slate-600 dark:text-slate-400 mb-4">
<h2 class="h3 mb-4 text-zinc-800 dark:text-zinc-100">Overview</h2>
<p class="text-zinc-600 dark:text-zinc-400 mb-4">
The API provides two ways to generate QR codes:
</p>
<ul class="list-disc list-inside space-y-2 text-slate-600 dark:text-slate-400">
<ul class="list-disc list-inside space-y-2 text-zinc-600 dark:text-zinc-400">
<li><strong>Biolink QR codes</strong> - Generate QR codes for your existing biolinks</li>
<li><strong>Custom URL QR codes</strong> - Generate QR codes for any URL</li>
</ul>
@ -72,24 +72,24 @@
{{-- Biolink QR --}}
<section id="biolink-qr" data-scrollspy-target class="mb-12">
<h2 class="h3 mb-4 text-slate-800 dark:text-slate-100">Biolink QR Codes</h2>
<p class="text-slate-600 dark:text-slate-400 mb-4">
<h2 class="h3 mb-4 text-zinc-800 dark:text-zinc-100">Biolink QR Codes</h2>
<p class="text-zinc-600 dark:text-zinc-400 mb-4">
Get QR code data for an existing biolink:
</p>
<div class="bg-slate-800 rounded-sm overflow-hidden mb-4">
<div class="flex items-center justify-between px-4 py-2 border-b border-slate-700">
<span class="text-sm text-slate-400">cURL</span>
<div class="bg-zinc-800 rounded-sm overflow-hidden mb-4">
<div class="flex items-center justify-between px-4 py-2 border-b border-zinc-700">
<span class="text-sm text-zinc-400">cURL</span>
</div>
<pre class="overflow-x-auto p-4 text-sm"><code class="font-pt-mono text-slate-300"><span class="text-teal-400">curl</span> <span class="text-slate-500">--request</span> GET \
<span class="text-slate-500">--url</span> <span class="text-amber-400">'https://api.host.uk.com/api/v1/bio/1/qr'</span> \
<span class="text-slate-500">--header</span> <span class="text-amber-400">'Authorization: Bearer YOUR_API_KEY'</span></code></pre>
<pre class="overflow-x-auto p-4 text-sm"><code class="font-pt-mono text-zinc-300"><span class="text-teal-400">curl</span> <span class="text-zinc-500">--request</span> GET \
<span class="text-zinc-500">--url</span> <span class="text-amber-400">'https://api.lthn.ai/api/v1/bio/1/qr'</span> \
<span class="text-zinc-500">--header</span> <span class="text-amber-400">'Authorization: Bearer YOUR_API_KEY'</span></code></pre>
</div>
<p class="text-slate-600 dark:text-slate-400 mb-4">Response:</p>
<p class="text-zinc-600 dark:text-zinc-400 mb-4">Response:</p>
<div class="bg-slate-800 rounded-sm overflow-hidden">
<pre class="overflow-x-auto p-4 text-sm"><code class="font-pt-mono text-slate-300">{
<div class="bg-zinc-800 rounded-sm overflow-hidden">
<pre class="overflow-x-auto p-4 text-sm"><code class="font-pt-mono text-zinc-300">{
<span class="text-blue-400">"data"</span>: {
<span class="text-blue-400">"svg"</span>: <span class="text-green-400">"&lt;svg&gt;...&lt;/svg&gt;"</span>,
<span class="text-blue-400">"url"</span>: <span class="text-green-400">"https://example.com/mypage"</span>
@ -100,20 +100,20 @@
{{-- Custom QR --}}
<section id="custom-qr" data-scrollspy-target class="mb-12">
<h2 class="h3 mb-4 text-slate-800 dark:text-slate-100">Custom URL QR Codes</h2>
<p class="text-slate-600 dark:text-slate-400 mb-4">
<h2 class="h3 mb-4 text-zinc-800 dark:text-zinc-100">Custom URL QR Codes</h2>
<p class="text-zinc-600 dark:text-zinc-400 mb-4">
Generate a QR code for any URL:
</p>
<div class="bg-slate-800 rounded-sm overflow-hidden mb-4">
<div class="flex items-center justify-between px-4 py-2 border-b border-slate-700">
<span class="text-sm text-slate-400">cURL</span>
<div class="bg-zinc-800 rounded-sm overflow-hidden mb-4">
<div class="flex items-center justify-between px-4 py-2 border-b border-zinc-700">
<span class="text-sm text-zinc-400">cURL</span>
</div>
<pre class="overflow-x-auto p-4 text-sm"><code class="font-pt-mono text-slate-300"><span class="text-teal-400">curl</span> <span class="text-slate-500">--request</span> POST \
<span class="text-slate-500">--url</span> <span class="text-amber-400">'https://api.host.uk.com/api/v1/qr/generate'</span> \
<span class="text-slate-500">--header</span> <span class="text-amber-400">'Authorization: Bearer YOUR_API_KEY'</span> \
<span class="text-slate-500">--header</span> <span class="text-amber-400">'Content-Type: application/json'</span> \
<span class="text-slate-500">--data</span> <span class="text-amber-400">'{
<pre class="overflow-x-auto p-4 text-sm"><code class="font-pt-mono text-zinc-300"><span class="text-teal-400">curl</span> <span class="text-zinc-500">--request</span> POST \
<span class="text-zinc-500">--url</span> <span class="text-amber-400">'https://api.lthn.ai/api/v1/qr/generate'</span> \
<span class="text-zinc-500">--header</span> <span class="text-amber-400">'Authorization: Bearer YOUR_API_KEY'</span> \
<span class="text-zinc-500">--header</span> <span class="text-amber-400">'Content-Type: application/json'</span> \
<span class="text-zinc-500">--data</span> <span class="text-amber-400">'{
"url": "https://example.com",
"format": "svg",
"size": 300
@ -123,40 +123,40 @@
{{-- Options --}}
<section id="options" data-scrollspy-target class="mb-12">
<h2 class="h3 mb-4 text-slate-800 dark:text-slate-100">Customisation Options</h2>
<p class="text-slate-600 dark:text-slate-400 mb-4">
<h2 class="h3 mb-4 text-zinc-800 dark:text-zinc-100">Customisation Options</h2>
<p class="text-zinc-600 dark:text-zinc-400 mb-4">
Available customisation parameters:
</p>
<div class="overflow-x-auto">
<table class="w-full text-sm">
<thead>
<tr class="border-b border-slate-200 dark:border-slate-700">
<th class="text-left py-3 px-4 font-medium text-slate-800 dark:text-slate-200">Parameter</th>
<th class="text-left py-3 px-4 font-medium text-slate-800 dark:text-slate-200">Type</th>
<th class="text-left py-3 px-4 font-medium text-slate-800 dark:text-slate-200">Description</th>
<tr class="border-b border-zinc-200 dark:border-zinc-700">
<th class="text-left py-3 px-4 font-medium text-zinc-800 dark:text-zinc-200">Parameter</th>
<th class="text-left py-3 px-4 font-medium text-zinc-800 dark:text-zinc-200">Type</th>
<th class="text-left py-3 px-4 font-medium text-zinc-800 dark:text-zinc-200">Description</th>
</tr>
</thead>
<tbody class="divide-y divide-slate-200 dark:divide-slate-700">
<tbody class="divide-y divide-zinc-200 dark:divide-zinc-700">
<tr>
<td class="py-3 px-4"><code class="px-1.5 py-0.5 bg-slate-100 dark:bg-slate-800 rounded text-xs">format</code></td>
<td class="py-3 px-4 text-slate-600 dark:text-slate-400">string</td>
<td class="py-3 px-4 text-slate-600 dark:text-slate-400">Output format: <code>svg</code> or <code>png</code></td>
<td class="py-3 px-4"><code class="px-1.5 py-0.5 bg-zinc-100 dark:bg-zinc-800 rounded text-xs">format</code></td>
<td class="py-3 px-4 text-zinc-600 dark:text-zinc-400">string</td>
<td class="py-3 px-4 text-zinc-600 dark:text-zinc-400">Output format: <code>svg</code> or <code>png</code></td>
</tr>
<tr>
<td class="py-3 px-4"><code class="px-1.5 py-0.5 bg-slate-100 dark:bg-slate-800 rounded text-xs">size</code></td>
<td class="py-3 px-4 text-slate-600 dark:text-slate-400">integer</td>
<td class="py-3 px-4 text-slate-600 dark:text-slate-400">Size in pixels (100-2000)</td>
<td class="py-3 px-4"><code class="px-1.5 py-0.5 bg-zinc-100 dark:bg-zinc-800 rounded text-xs">size</code></td>
<td class="py-3 px-4 text-zinc-600 dark:text-zinc-400">integer</td>
<td class="py-3 px-4 text-zinc-600 dark:text-zinc-400">Size in pixels (100-2000)</td>
</tr>
<tr>
<td class="py-3 px-4"><code class="px-1.5 py-0.5 bg-slate-100 dark:bg-slate-800 rounded text-xs">color</code></td>
<td class="py-3 px-4 text-slate-600 dark:text-slate-400">string</td>
<td class="py-3 px-4 text-slate-600 dark:text-slate-400">Foreground colour (hex)</td>
<td class="py-3 px-4"><code class="px-1.5 py-0.5 bg-zinc-100 dark:bg-zinc-800 rounded text-xs">color</code></td>
<td class="py-3 px-4 text-zinc-600 dark:text-zinc-400">string</td>
<td class="py-3 px-4 text-zinc-600 dark:text-zinc-400">Foreground colour (hex)</td>
</tr>
<tr>
<td class="py-3 px-4"><code class="px-1.5 py-0.5 bg-slate-100 dark:bg-slate-800 rounded text-xs">background</code></td>
<td class="py-3 px-4 text-slate-600 dark:text-slate-400">string</td>
<td class="py-3 px-4 text-slate-600 dark:text-slate-400">Background colour (hex)</td>
<td class="py-3 px-4"><code class="px-1.5 py-0.5 bg-zinc-100 dark:bg-zinc-800 rounded text-xs">background</code></td>
<td class="py-3 px-4 text-zinc-600 dark:text-zinc-400">string</td>
<td class="py-3 px-4 text-zinc-600 dark:text-zinc-400">Background colour (hex)</td>
</tr>
</tbody>
</table>
@ -165,32 +165,32 @@
{{-- Download --}}
<section id="download" data-scrollspy-target class="mb-12">
<h2 class="h3 mb-4 text-slate-800 dark:text-slate-100">Download Formats</h2>
<p class="text-slate-600 dark:text-slate-400 mb-4">
<h2 class="h3 mb-4 text-zinc-800 dark:text-zinc-100">Download Formats</h2>
<p class="text-zinc-600 dark:text-zinc-400 mb-4">
Download QR codes directly as image files:
</p>
<div class="bg-slate-800 rounded-sm overflow-hidden mb-4">
<div class="flex items-center justify-between px-4 py-2 border-b border-slate-700">
<span class="text-sm text-slate-400">cURL</span>
<div class="bg-zinc-800 rounded-sm overflow-hidden mb-4">
<div class="flex items-center justify-between px-4 py-2 border-b border-zinc-700">
<span class="text-sm text-zinc-400">cURL</span>
</div>
<pre class="overflow-x-auto p-4 text-sm"><code class="font-pt-mono text-slate-300"><span class="text-teal-400">curl</span> <span class="text-slate-500">--request</span> GET \
<span class="text-slate-500">--url</span> <span class="text-amber-400">'https://api.host.uk.com/api/v1/bio/1/qr/download?format=png&size=500'</span> \
<span class="text-slate-500">--header</span> <span class="text-amber-400">'Authorization: Bearer YOUR_API_KEY'</span> \
<span class="text-slate-500">--output</span> <span class="text-amber-400">qrcode.png</span></code></pre>
<pre class="overflow-x-auto p-4 text-sm"><code class="font-pt-mono text-zinc-300"><span class="text-teal-400">curl</span> <span class="text-zinc-500">--request</span> GET \
<span class="text-zinc-500">--url</span> <span class="text-amber-400">'https://api.lthn.ai/api/v1/bio/1/qr/download?format=png&size=500'</span> \
<span class="text-zinc-500">--header</span> <span class="text-amber-400">'Authorization: Bearer YOUR_API_KEY'</span> \
<span class="text-zinc-500">--output</span> <span class="text-amber-400">qrcode.png</span></code></pre>
</div>
<p class="text-slate-600 dark:text-slate-400">
<p class="text-zinc-600 dark:text-zinc-400">
The response is binary image data with appropriate Content-Type header.
</p>
</section>
{{-- Next steps --}}
<div class="flex items-center justify-between pt-8 border-t border-slate-200 dark:border-slate-700">
<a href="{{ route('api.guides.biolinks') }}" class="text-slate-600 hover:text-slate-900 dark:text-slate-400 dark:hover:text-slate-200">
&larr; Managing Biolinks
<div class="flex items-center justify-between pt-8 border-t border-zinc-200 dark:border-zinc-700">
<a href="{{ route('api.guides.authentication') }}" class="text-zinc-600 hover:text-zinc-900 dark:text-zinc-400 dark:hover:text-zinc-200">
&larr; Authentication
</a>
<a href="{{ route('api.guides.webhooks') }}" class="text-blue-600 hover:text-blue-700 dark:hover:text-blue-500 font-medium">
<a href="{{ route('api.guides.webhooks') }}" class="text-cyan-600 hover:text-cyan-700 dark:hover:text-blue-500 font-medium">
Webhooks &rarr;
</a>
</div>

View file

@ -6,32 +6,32 @@
<div class="flex">
{{-- Sidebar --}}
<aside class="hidden lg:block fixed left-0 top-16 md:top-20 bottom-0 w-64 border-r border-slate-200 dark:border-slate-800">
<aside class="hidden lg:block fixed left-0 top-16 md:top-20 bottom-0 w-64 border-r border-zinc-200 dark:border-zinc-800">
<div class="h-full px-4 py-8 overflow-y-auto no-scrollbar">
<nav>
<ul class="space-y-2">
<li>
<a href="#prerequisites" data-scrollspy-link class="block px-3 py-2 text-sm text-slate-600 hover:text-slate-900 dark:text-slate-400 dark:hover:text-slate-200 rounded-sm relative before:absolute before:inset-y-1 before:left-0 before:w-0.5 before:rounded-full">
<a href="#prerequisites" data-scrollspy-link class="block px-3 py-2 text-sm text-zinc-600 hover:text-zinc-900 dark:text-zinc-400 dark:hover:text-zinc-200 rounded-sm relative before:absolute before:inset-y-1 before:left-0 before:w-0.5 before:rounded-full">
Prerequisites
</a>
</li>
<li>
<a href="#create-api-key" data-scrollspy-link class="block px-3 py-2 text-sm text-slate-600 hover:text-slate-900 dark:text-slate-400 dark:hover:text-slate-200 rounded-sm relative before:absolute before:inset-y-1 before:left-0 before:w-0.5 before:rounded-full">
<a href="#create-api-key" data-scrollspy-link class="block px-3 py-2 text-sm text-zinc-600 hover:text-zinc-900 dark:text-zinc-400 dark:hover:text-zinc-200 rounded-sm relative before:absolute before:inset-y-1 before:left-0 before:w-0.5 before:rounded-full">
Create an API Key
</a>
</li>
<li>
<a href="#first-request" data-scrollspy-link class="block px-3 py-2 text-sm text-slate-600 hover:text-slate-900 dark:text-slate-400 dark:hover:text-slate-200 rounded-sm relative before:absolute before:inset-y-1 before:left-0 before:w-0.5 before:rounded-full">
<a href="#first-request" data-scrollspy-link class="block px-3 py-2 text-sm text-zinc-600 hover:text-zinc-900 dark:text-zinc-400 dark:hover:text-zinc-200 rounded-sm relative before:absolute before:inset-y-1 before:left-0 before:w-0.5 before:rounded-full">
Make Your First Request
</a>
</li>
<li>
<a href="#create-biolink" data-scrollspy-link class="block px-3 py-2 text-sm text-slate-600 hover:text-slate-900 dark:text-slate-400 dark:hover:text-slate-200 rounded-sm relative before:absolute before:inset-y-1 before:left-0 before:w-0.5 before:rounded-full">
Create a Biolink
<a href="#store-memory" data-scrollspy-link class="block px-3 py-2 text-sm text-zinc-600 hover:text-zinc-900 dark:text-zinc-400 dark:hover:text-zinc-200 rounded-sm relative before:absolute before:inset-y-1 before:left-0 before:w-0.5 before:rounded-full">
Store a Memory
</a>
</li>
<li>
<a href="#next-steps" data-scrollspy-link class="block px-3 py-2 text-sm text-slate-600 hover:text-slate-900 dark:text-slate-400 dark:hover:text-slate-200 rounded-sm relative before:absolute before:inset-y-1 before:left-0 before:w-0.5 before:rounded-full">
<a href="#next-steps" data-scrollspy-link class="block px-3 py-2 text-sm text-zinc-600 hover:text-zinc-900 dark:text-zinc-400 dark:hover:text-zinc-200 rounded-sm relative before:absolute before:inset-y-1 before:left-0 before:w-0.5 before:rounded-full">
Next Steps
</a>
</li>
@ -47,24 +47,24 @@
{{-- Breadcrumb --}}
<nav class="mb-8">
<ol class="flex items-center gap-2 text-sm">
<li><a href="{{ route('api.guides') }}" class="text-slate-500 hover:text-slate-700 dark:text-slate-400 dark:hover:text-slate-200">Guides</a></li>
<li class="text-slate-400">/</li>
<li class="text-slate-800 dark:text-slate-200">Quick Start</li>
<li><a href="{{ route('api.guides') }}" class="text-zinc-500 hover:text-zinc-700 dark:text-zinc-400 dark:hover:text-zinc-200">Guides</a></li>
<li class="text-zinc-400">/</li>
<li class="text-zinc-800 dark:text-zinc-200">Quick Start</li>
</ol>
</nav>
<h1 class="h1 mb-4 text-slate-800 dark:text-slate-100">Quick Start</h1>
<p class="text-xl text-slate-600 dark:text-slate-400 mb-12">
<h1 class="h1 mb-4 text-zinc-800 dark:text-zinc-100">Quick Start</h1>
<p class="text-xl text-zinc-600 dark:text-zinc-400 mb-12">
Get up and running with the API in under 5 minutes.
</p>
{{-- Prerequisites --}}
<section id="prerequisites" data-scrollspy-target class="mb-12">
<h2 class="h3 mb-4 text-slate-800 dark:text-slate-100">Prerequisites</h2>
<p class="text-slate-600 dark:text-slate-400 mb-4">
<h2 class="h3 mb-4 text-zinc-800 dark:text-zinc-100">Prerequisites</h2>
<p class="text-zinc-600 dark:text-zinc-400 mb-4">
Before you begin, you'll need:
</p>
<ul class="list-disc list-inside space-y-2 text-slate-600 dark:text-slate-400 mb-4">
<ul class="list-disc list-inside space-y-2 text-zinc-600 dark:text-zinc-400 mb-4">
<li>An account with API access</li>
<li>A workspace (created automatically on signup)</li>
<li>cURL or any HTTP client</li>
@ -73,16 +73,16 @@
{{-- Create API Key --}}
<section id="create-api-key" data-scrollspy-target class="mb-12">
<h2 class="h3 mb-4 text-slate-800 dark:text-slate-100">Create an API Key</h2>
<p class="text-slate-600 dark:text-slate-400 mb-4">
<h2 class="h3 mb-4 text-zinc-800 dark:text-zinc-100">Create an API Key</h2>
<p class="text-zinc-600 dark:text-zinc-400 mb-4">
Navigate to your workspace settings and create a new API key:
</p>
<ol class="list-decimal list-inside space-y-2 text-slate-600 dark:text-slate-400 mb-6">
<li>Go to <strong>Settings API Keys</strong></li>
<ol class="list-decimal list-inside space-y-2 text-zinc-600 dark:text-zinc-400 mb-6">
<li>Go to <strong>Settings &rarr; API Keys</strong></li>
<li>Click <strong>Create API Key</strong></li>
<li>Give it a name (e.g., "Development")</li>
<li>Select the scopes you need (read, write, delete)</li>
<li>Copy the key - it won't be shown again!</li>
<li>Copy the key &mdash; it won't be shown again!</li>
</ol>
{{-- Note box --}}
@ -100,88 +100,87 @@
{{-- First Request --}}
<section id="first-request" data-scrollspy-target class="mb-12">
<h2 class="h3 mb-4 text-slate-800 dark:text-slate-100">Make Your First Request</h2>
<p class="text-slate-600 dark:text-slate-400 mb-4">
Let's verify your API key by listing your workspaces:
<h2 class="h3 mb-4 text-zinc-800 dark:text-zinc-100">Make Your First Request</h2>
<p class="text-zinc-600 dark:text-zinc-400 mb-4">
Let's verify your API key by searching agent memories:
</p>
<div class="bg-slate-800 rounded-sm overflow-hidden mb-4">
<div class="flex items-center justify-between px-4 py-2 border-b border-slate-700">
<span class="text-sm text-slate-400">cURL</span>
<div class="bg-zinc-800 rounded-sm overflow-hidden mb-4">
<div class="flex items-center justify-between px-4 py-2 border-b border-zinc-700">
<span class="text-sm text-zinc-400">cURL</span>
</div>
<pre class="overflow-x-auto p-4 text-sm"><code class="font-pt-mono text-slate-300"><span class="text-teal-400">curl</span> <span class="text-slate-500">--request</span> GET \
<span class="text-slate-500">--url</span> <span class="text-amber-400">'https://api.host.uk.com/api/v1/workspaces/current'</span> \
<span class="text-slate-500">--header</span> <span class="text-amber-400">'Authorization: Bearer YOUR_API_KEY'</span></code></pre>
<pre class="overflow-x-auto p-4 text-sm"><code class="font-pt-mono text-zinc-300"><span class="text-teal-400">curl</span> <span class="text-zinc-500">--request</span> POST \
<span class="text-zinc-500">--url</span> <span class="text-amber-400">'https://api.lthn.ai/v1/brain/recall'</span> \
<span class="text-zinc-500">--header</span> <span class="text-amber-400">'Authorization: Bearer hk_your_api_key'</span> \
<span class="text-zinc-500">--header</span> <span class="text-amber-400">'Content-Type: application/json'</span> \
<span class="text-zinc-500">--data</span> <span class="text-amber-400">'{"query": "hello world"}'</span></code></pre>
</div>
<p class="text-slate-600 dark:text-slate-400 mb-4">
<p class="text-zinc-600 dark:text-zinc-400 mb-4">
You should receive a response like:
</p>
<div class="bg-slate-800 rounded-sm overflow-hidden">
<div class="flex items-center justify-between px-4 py-2 border-b border-slate-700">
<span class="text-sm text-slate-400">Response</span>
<div class="bg-zinc-800 rounded-sm overflow-hidden">
<div class="flex items-center justify-between px-4 py-2 border-b border-zinc-700">
<span class="text-sm text-zinc-400">Response</span>
</div>
<pre class="overflow-x-auto p-4 text-sm"><code class="font-pt-mono text-slate-300">{
<span class="text-blue-400">"data"</span>: {
<span class="text-blue-400">"id"</span>: <span class="text-amber-400">1</span>,
<span class="text-blue-400">"name"</span>: <span class="text-green-400">"My Workspace"</span>,
<span class="text-blue-400">"slug"</span>: <span class="text-green-400">"my-workspace-abc123"</span>,
<span class="text-blue-400">"is_active"</span>: <span class="text-purple-400">true</span>
}
<pre class="overflow-x-auto p-4 text-sm"><code class="font-pt-mono text-zinc-300">{
<span class="text-blue-400">"memories"</span>: [],
<span class="text-blue-400">"scores"</span>: {}
}</code></pre>
</div>
</section>
{{-- Create Biolink --}}
<section id="create-biolink" data-scrollspy-target class="mb-12">
<h2 class="h3 mb-4 text-slate-800 dark:text-slate-100">Create a Biolink</h2>
<p class="text-slate-600 dark:text-slate-400 mb-4">
Now let's create your first biolink programmatically:
{{-- Store Memory --}}
<section id="store-memory" data-scrollspy-target class="mb-12">
<h2 class="h3 mb-4 text-zinc-800 dark:text-zinc-100">Store a Memory</h2>
<p class="text-zinc-600 dark:text-zinc-400 mb-4">
Now let's store your first memory in the brain:
</p>
<div class="bg-slate-800 rounded-sm overflow-hidden mb-4">
<div class="flex items-center justify-between px-4 py-2 border-b border-slate-700">
<span class="text-sm text-slate-400">cURL</span>
<div class="bg-zinc-800 rounded-sm overflow-hidden mb-4">
<div class="flex items-center justify-between px-4 py-2 border-b border-zinc-700">
<span class="text-sm text-zinc-400">cURL</span>
</div>
<pre class="overflow-x-auto p-4 text-sm"><code class="font-pt-mono text-slate-300"><span class="text-teal-400">curl</span> <span class="text-slate-500">--request</span> POST \
<span class="text-slate-500">--url</span> <span class="text-amber-400">'https://api.host.uk.com/api/v1/bio'</span> \
<span class="text-slate-500">--header</span> <span class="text-amber-400">'Authorization: Bearer YOUR_API_KEY'</span> \
<span class="text-slate-500">--header</span> <span class="text-amber-400">'Content-Type: application/json'</span> \
<span class="text-slate-500">--data</span> <span class="text-amber-400">'{
"url": "mypage",
"type": "biolink"
<pre class="overflow-x-auto p-4 text-sm"><code class="font-pt-mono text-zinc-300"><span class="text-teal-400">curl</span> <span class="text-zinc-500">--request</span> POST \
<span class="text-zinc-500">--url</span> <span class="text-amber-400">'https://api.lthn.ai/v1/brain/remember'</span> \
<span class="text-zinc-500">--header</span> <span class="text-amber-400">'Authorization: Bearer hk_your_api_key'</span> \
<span class="text-zinc-500">--header</span> <span class="text-amber-400">'Content-Type: application/json'</span> \
<span class="text-zinc-500">--data</span> <span class="text-amber-400">'{
"content": "Go uses structural typing",
"type": "fact",
"tags": ["go", "typing"]
}'</span></code></pre>
</div>
<p class="text-slate-600 dark:text-slate-400">
This creates a new biolink page at your configured short URL.
<p class="text-zinc-600 dark:text-zinc-400">
This stores a fact in the vector database. You can then recall it with a semantic query.
</p>
</section>
{{-- Next Steps --}}
<section id="next-steps" data-scrollspy-target class="mb-12">
<h2 class="h3 mb-4 text-slate-800 dark:text-slate-100">Next Steps</h2>
<p class="text-slate-600 dark:text-slate-400 mb-6">
<h2 class="h3 mb-4 text-zinc-800 dark:text-zinc-100">Next Steps</h2>
<p class="text-zinc-600 dark:text-zinc-400 mb-6">
Now that you've made your first API calls, explore more:
</p>
<div class="grid sm:grid-cols-2 gap-4">
<a href="{{ route('api.guides.biolinks') }}" class="p-4 bg-slate-50 dark:bg-slate-800 border border-slate-200 dark:border-slate-700 rounded-sm hover:border-blue-300 dark:hover:border-blue-600">
<h3 class="font-medium text-slate-800 dark:text-slate-200 mb-1">Managing Biolinks</h3>
<p class="text-sm text-slate-600 dark:text-slate-400">Add blocks, update settings, and customise themes.</p>
<a href="{{ route('api.guides.authentication') }}" class="p-4 bg-zinc-50 dark:bg-zinc-800 border border-zinc-200 dark:border-zinc-700 rounded-sm hover:border-cyan-300 dark:hover:border-cyan-600">
<h3 class="font-medium text-zinc-800 dark:text-zinc-200 mb-1">Authentication</h3>
<p class="text-sm text-zinc-600 dark:text-zinc-400">API key scopes, security best practices.</p>
</a>
<a href="{{ route('api.guides.qrcodes') }}" class="p-4 bg-slate-50 dark:bg-slate-800 border border-slate-200 dark:border-slate-700 rounded-sm hover:border-blue-300 dark:hover:border-blue-600">
<h3 class="font-medium text-slate-800 dark:text-slate-200 mb-1">QR Code Generation</h3>
<p class="text-sm text-slate-600 dark:text-slate-400">Generate customised QR codes for your biolinks.</p>
<a href="{{ route('api.reference') }}" class="p-4 bg-zinc-50 dark:bg-zinc-800 border border-zinc-200 dark:border-zinc-700 rounded-sm hover:border-cyan-300 dark:hover:border-cyan-600">
<h3 class="font-medium text-zinc-800 dark:text-zinc-200 mb-1">API Reference</h3>
<p class="text-sm text-zinc-600 dark:text-zinc-400">Complete documentation of all endpoints.</p>
</a>
<a href="{{ route('api.reference') }}" class="p-4 bg-slate-50 dark:bg-slate-800 border border-slate-200 dark:border-slate-700 rounded-sm hover:border-blue-300 dark:hover:border-blue-600">
<h3 class="font-medium text-slate-800 dark:text-slate-200 mb-1">API Reference</h3>
<p class="text-sm text-slate-600 dark:text-slate-400">Complete documentation of all endpoints.</p>
<a href="{{ route('api.guides.webhooks') }}" class="p-4 bg-zinc-50 dark:bg-zinc-800 border border-zinc-200 dark:border-zinc-700 rounded-sm hover:border-cyan-300 dark:hover:border-cyan-600">
<h3 class="font-medium text-zinc-800 dark:text-zinc-200 mb-1">Webhooks</h3>
<p class="text-sm text-zinc-600 dark:text-zinc-400">Receive real-time event notifications.</p>
</a>
<a href="{{ route('api.swagger') }}" class="p-4 bg-slate-50 dark:bg-slate-800 border border-slate-200 dark:border-slate-700 rounded-sm hover:border-blue-300 dark:hover:border-blue-600">
<h3 class="font-medium text-slate-800 dark:text-slate-200 mb-1">Swagger UI</h3>
<p class="text-sm text-slate-600 dark:text-slate-400">Interactive API explorer with try-it-out.</p>
<a href="{{ route('api.swagger') }}" class="p-4 bg-zinc-50 dark:bg-zinc-800 border border-zinc-200 dark:border-zinc-700 rounded-sm hover:border-cyan-300 dark:hover:border-cyan-600">
<h3 class="font-medium text-zinc-800 dark:text-zinc-200 mb-1">Swagger UI</h3>
<p class="text-sm text-zinc-600 dark:text-zinc-400">Interactive API explorer with try-it-out.</p>
</a>
</div>
</section>

View file

@ -6,47 +6,47 @@
<div class="flex">
{{-- Sidebar --}}
<aside class="hidden lg:block fixed left-0 top-16 md:top-20 bottom-0 w-64 border-r border-slate-200 dark:border-slate-800">
<aside class="hidden lg:block fixed left-0 top-16 md:top-20 bottom-0 w-64 border-r border-zinc-200 dark:border-zinc-800">
<div class="h-full px-4 py-8 overflow-y-auto no-scrollbar">
<nav>
<ul class="space-y-2">
<li>
<a href="#overview" data-scrollspy-link class="block px-3 py-2 text-sm text-slate-600 hover:text-slate-900 dark:text-slate-400 dark:hover:text-slate-200 rounded-sm relative before:absolute before:inset-y-1 before:left-0 before:w-0.5 before:rounded-full">
<a href="#overview" data-scrollspy-link class="block px-3 py-2 text-sm text-zinc-600 hover:text-zinc-900 dark:text-zinc-400 dark:hover:text-zinc-200 rounded-sm relative before:absolute before:inset-y-1 before:left-0 before:w-0.5 before:rounded-full">
Overview
</a>
</li>
<li>
<a href="#setup" data-scrollspy-link class="block px-3 py-2 text-sm text-slate-600 hover:text-slate-900 dark:text-slate-400 dark:hover:text-slate-200 rounded-sm relative before:absolute before:inset-y-1 before:left-0 before:w-0.5 before:rounded-full">
<a href="#setup" data-scrollspy-link class="block px-3 py-2 text-sm text-zinc-600 hover:text-zinc-900 dark:text-zinc-400 dark:hover:text-zinc-200 rounded-sm relative before:absolute before:inset-y-1 before:left-0 before:w-0.5 before:rounded-full">
Setup
</a>
</li>
<li>
<a href="#events" data-scrollspy-link class="block px-3 py-2 text-sm text-slate-600 hover:text-slate-900 dark:text-slate-400 dark:hover:text-slate-200 rounded-sm relative before:absolute before:inset-y-1 before:left-0 before:w-0.5 before:rounded-full">
<a href="#events" data-scrollspy-link class="block px-3 py-2 text-sm text-zinc-600 hover:text-zinc-900 dark:text-zinc-400 dark:hover:text-zinc-200 rounded-sm relative before:absolute before:inset-y-1 before:left-0 before:w-0.5 before:rounded-full">
Event Types
</a>
</li>
<li>
<a href="#payload" data-scrollspy-link class="block px-3 py-2 text-sm text-slate-600 hover:text-slate-900 dark:text-slate-400 dark:hover:text-slate-200 rounded-sm relative before:absolute before:inset-y-1 before:left-0 before:w-0.5 before:rounded-full">
<a href="#payload" data-scrollspy-link class="block px-3 py-2 text-sm text-zinc-600 hover:text-zinc-900 dark:text-zinc-400 dark:hover:text-zinc-200 rounded-sm relative before:absolute before:inset-y-1 before:left-0 before:w-0.5 before:rounded-full">
Payload Format
</a>
</li>
<li>
<a href="#headers" data-scrollspy-link class="block px-3 py-2 text-sm text-slate-600 hover:text-slate-900 dark:text-slate-400 dark:hover:text-slate-200 rounded-sm relative before:absolute before:inset-y-1 before:left-0 before:w-0.5 before:rounded-full">
<a href="#headers" data-scrollspy-link class="block px-3 py-2 text-sm text-zinc-600 hover:text-zinc-900 dark:text-zinc-400 dark:hover:text-zinc-200 rounded-sm relative before:absolute before:inset-y-1 before:left-0 before:w-0.5 before:rounded-full">
Request Headers
</a>
</li>
<li>
<a href="#verification" data-scrollspy-link class="block px-3 py-2 text-sm text-slate-600 hover:text-slate-900 dark:text-slate-400 dark:hover:text-slate-200 rounded-sm relative before:absolute before:inset-y-1 before:left-0 before:w-0.5 before:rounded-full">
<a href="#verification" data-scrollspy-link class="block px-3 py-2 text-sm text-zinc-600 hover:text-zinc-900 dark:text-zinc-400 dark:hover:text-zinc-200 rounded-sm relative before:absolute before:inset-y-1 before:left-0 before:w-0.5 before:rounded-full">
Signature Verification
</a>
</li>
<li>
<a href="#retry-policy" data-scrollspy-link class="block px-3 py-2 text-sm text-slate-600 hover:text-slate-900 dark:text-slate-400 dark:hover:text-slate-200 rounded-sm relative before:absolute before:inset-y-1 before:left-0 before:w-0.5 before:rounded-full">
<a href="#retry-policy" data-scrollspy-link class="block px-3 py-2 text-sm text-zinc-600 hover:text-zinc-900 dark:text-zinc-400 dark:hover:text-zinc-200 rounded-sm relative before:absolute before:inset-y-1 before:left-0 before:w-0.5 before:rounded-full">
Retry Policy
</a>
</li>
<li>
<a href="#best-practices" data-scrollspy-link class="block px-3 py-2 text-sm text-slate-600 hover:text-slate-900 dark:text-slate-400 dark:hover:text-slate-200 rounded-sm relative before:absolute before:inset-y-1 before:left-0 before:w-0.5 before:rounded-full">
<a href="#best-practices" data-scrollspy-link class="block px-3 py-2 text-sm text-zinc-600 hover:text-zinc-900 dark:text-zinc-400 dark:hover:text-zinc-200 rounded-sm relative before:absolute before:inset-y-1 before:left-0 before:w-0.5 before:rounded-full">
Best Practices
</a>
</li>
@ -62,24 +62,24 @@
{{-- Breadcrumb --}}
<nav class="mb-8">
<ol class="flex items-center gap-2 text-sm">
<li><a href="{{ route('api.guides') }}" class="text-slate-500 hover:text-slate-700 dark:text-slate-400 dark:hover:text-slate-200">Guides</a></li>
<li class="text-slate-400">/</li>
<li class="text-slate-800 dark:text-slate-200">Webhooks</li>
<li><a href="{{ route('api.guides') }}" class="text-zinc-500 hover:text-zinc-700 dark:text-zinc-400 dark:hover:text-zinc-200">Guides</a></li>
<li class="text-zinc-400">/</li>
<li class="text-zinc-800 dark:text-zinc-200">Webhooks</li>
</ol>
</nav>
<h1 class="h1 mb-4 text-slate-800 dark:text-slate-100">Webhooks</h1>
<p class="text-xl text-slate-600 dark:text-slate-400 mb-12">
<h1 class="h1 mb-4 text-zinc-800 dark:text-zinc-100">Webhooks</h1>
<p class="text-xl text-zinc-600 dark:text-zinc-400 mb-12">
Receive real-time notifications for events in your workspace with cryptographically signed payloads.
</p>
{{-- Overview --}}
<section id="overview" data-scrollspy-target class="mb-12">
<h2 class="h3 mb-4 text-slate-800 dark:text-slate-100">Overview</h2>
<p class="text-slate-600 dark:text-slate-400 mb-4">
<h2 class="h3 mb-4 text-zinc-800 dark:text-zinc-100">Overview</h2>
<p class="text-zinc-600 dark:text-zinc-400 mb-4">
Webhooks allow your application to receive real-time HTTP callbacks when events occur in your workspace. Instead of polling the API, webhooks push data to your server as events happen.
</p>
<p class="text-slate-600 dark:text-slate-400 mb-4">
<p class="text-zinc-600 dark:text-zinc-400 mb-4">
All webhook requests are cryptographically signed using HMAC-SHA256, allowing you to verify that requests genuinely came from our platform and haven't been tampered with.
</p>
<div class="text-sm p-4 bg-amber-50 border border-amber-200 rounded-sm dark:bg-amber-900/20 dark:border-amber-800">
@ -96,11 +96,11 @@
{{-- Setup --}}
<section id="setup" data-scrollspy-target class="mb-12">
<h2 class="h3 mb-4 text-slate-800 dark:text-slate-100">Setup</h2>
<p class="text-slate-600 dark:text-slate-400 mb-4">
<h2 class="h3 mb-4 text-zinc-800 dark:text-zinc-100">Setup</h2>
<p class="text-zinc-600 dark:text-zinc-400 mb-4">
To configure webhooks:
</p>
<ol class="list-decimal list-inside space-y-2 text-slate-600 dark:text-slate-400 mb-4">
<ol class="list-decimal list-inside space-y-2 text-zinc-600 dark:text-zinc-400 mb-4">
<li>Go to <strong>Settings &rarr; Webhooks</strong> in your workspace</li>
<li>Click <strong>Add Webhook</strong></li>
<li>Enter your endpoint URL (must be HTTPS in production)</li>
@ -121,51 +121,51 @@
{{-- Events --}}
<section id="events" data-scrollspy-target class="mb-12">
<h2 class="h3 mb-4 text-slate-800 dark:text-slate-100">Event Types</h2>
<p class="text-slate-600 dark:text-slate-400 mb-4">
<h2 class="h3 mb-4 text-zinc-800 dark:text-zinc-100">Event Types</h2>
<p class="text-zinc-600 dark:text-zinc-400 mb-4">
Available webhook events:
</p>
<div class="overflow-x-auto">
<table class="w-full text-sm">
<thead>
<tr class="border-b border-slate-200 dark:border-slate-700">
<th class="text-left py-3 px-4 font-medium text-slate-800 dark:text-slate-200">Event</th>
<th class="text-left py-3 px-4 font-medium text-slate-800 dark:text-slate-200">Description</th>
<tr class="border-b border-zinc-200 dark:border-zinc-700">
<th class="text-left py-3 px-4 font-medium text-zinc-800 dark:text-zinc-200">Event</th>
<th class="text-left py-3 px-4 font-medium text-zinc-800 dark:text-zinc-200">Description</th>
</tr>
</thead>
<tbody class="divide-y divide-slate-200 dark:divide-slate-700">
<tbody class="divide-y divide-zinc-200 dark:divide-zinc-700">
<tr>
<td class="py-3 px-4"><code class="px-1.5 py-0.5 bg-slate-100 dark:bg-slate-800 rounded text-xs">bio.created</code></td>
<td class="py-3 px-4 text-slate-600 dark:text-slate-400">A new biolink was created</td>
<td class="py-3 px-4"><code class="px-1.5 py-0.5 bg-zinc-100 dark:bg-zinc-800 rounded text-xs">bio.created</code></td>
<td class="py-3 px-4 text-zinc-600 dark:text-zinc-400">A new biolink was created</td>
</tr>
<tr>
<td class="py-3 px-4"><code class="px-1.5 py-0.5 bg-slate-100 dark:bg-slate-800 rounded text-xs">bio.updated</code></td>
<td class="py-3 px-4 text-slate-600 dark:text-slate-400">A biolink was updated</td>
<td class="py-3 px-4"><code class="px-1.5 py-0.5 bg-zinc-100 dark:bg-zinc-800 rounded text-xs">bio.updated</code></td>
<td class="py-3 px-4 text-zinc-600 dark:text-zinc-400">A biolink was updated</td>
</tr>
<tr>
<td class="py-3 px-4"><code class="px-1.5 py-0.5 bg-slate-100 dark:bg-slate-800 rounded text-xs">bio.deleted</code></td>
<td class="py-3 px-4 text-slate-600 dark:text-slate-400">A biolink was deleted</td>
<td class="py-3 px-4"><code class="px-1.5 py-0.5 bg-zinc-100 dark:bg-zinc-800 rounded text-xs">bio.deleted</code></td>
<td class="py-3 px-4 text-zinc-600 dark:text-zinc-400">A biolink was deleted</td>
</tr>
<tr>
<td class="py-3 px-4"><code class="px-1.5 py-0.5 bg-slate-100 dark:bg-slate-800 rounded text-xs">link.created</code></td>
<td class="py-3 px-4 text-slate-600 dark:text-slate-400">A new link was created</td>
<td class="py-3 px-4"><code class="px-1.5 py-0.5 bg-zinc-100 dark:bg-zinc-800 rounded text-xs">link.created</code></td>
<td class="py-3 px-4 text-zinc-600 dark:text-zinc-400">A new link was created</td>
</tr>
<tr>
<td class="py-3 px-4"><code class="px-1.5 py-0.5 bg-slate-100 dark:bg-slate-800 rounded text-xs">link.clicked</code></td>
<td class="py-3 px-4 text-slate-600 dark:text-slate-400">A link was clicked (high volume)</td>
<td class="py-3 px-4"><code class="px-1.5 py-0.5 bg-zinc-100 dark:bg-zinc-800 rounded text-xs">link.clicked</code></td>
<td class="py-3 px-4 text-zinc-600 dark:text-zinc-400">A link was clicked (high volume)</td>
</tr>
<tr>
<td class="py-3 px-4"><code class="px-1.5 py-0.5 bg-slate-100 dark:bg-slate-800 rounded text-xs">qrcode.created</code></td>
<td class="py-3 px-4 text-slate-600 dark:text-slate-400">A QR code was generated</td>
<td class="py-3 px-4"><code class="px-1.5 py-0.5 bg-zinc-100 dark:bg-zinc-800 rounded text-xs">qrcode.created</code></td>
<td class="py-3 px-4 text-zinc-600 dark:text-zinc-400">A QR code was generated</td>
</tr>
<tr>
<td class="py-3 px-4"><code class="px-1.5 py-0.5 bg-slate-100 dark:bg-slate-800 rounded text-xs">qrcode.scanned</code></td>
<td class="py-3 px-4 text-slate-600 dark:text-slate-400">A QR code was scanned (high volume)</td>
<td class="py-3 px-4"><code class="px-1.5 py-0.5 bg-zinc-100 dark:bg-zinc-800 rounded text-xs">qrcode.scanned</code></td>
<td class="py-3 px-4 text-zinc-600 dark:text-zinc-400">A QR code was scanned (high volume)</td>
</tr>
<tr>
<td class="py-3 px-4"><code class="px-1.5 py-0.5 bg-slate-100 dark:bg-slate-800 rounded text-xs">*</code></td>
<td class="py-3 px-4 text-slate-600 dark:text-slate-400">Subscribe to all events (wildcard)</td>
<td class="py-3 px-4"><code class="px-1.5 py-0.5 bg-zinc-100 dark:bg-zinc-800 rounded text-xs">*</code></td>
<td class="py-3 px-4 text-zinc-600 dark:text-zinc-400">Subscribe to all events (wildcard)</td>
</tr>
</tbody>
</table>
@ -174,13 +174,13 @@
{{-- Payload --}}
<section id="payload" data-scrollspy-target class="mb-12">
<h2 class="h3 mb-4 text-slate-800 dark:text-slate-100">Payload Format</h2>
<p class="text-slate-600 dark:text-slate-400 mb-4">
<h2 class="h3 mb-4 text-zinc-800 dark:text-zinc-100">Payload Format</h2>
<p class="text-zinc-600 dark:text-zinc-400 mb-4">
Webhook payloads are sent as JSON with a consistent structure:
</p>
<div class="bg-slate-800 rounded-sm overflow-hidden">
<pre class="overflow-x-auto p-4 text-sm"><code class="font-pt-mono text-slate-300">{
<div class="bg-zinc-800 rounded-sm overflow-hidden">
<pre class="overflow-x-auto p-4 text-sm"><code class="font-pt-mono text-zinc-300">{
<span class="text-blue-400">"id"</span>: <span class="text-green-400">"evt_abc123xyz456"</span>,
<span class="text-blue-400">"type"</span>: <span class="text-green-400">"bio.created"</span>,
<span class="text-blue-400">"created_at"</span>: <span class="text-green-400">"2024-01-15T10:30:00Z"</span>,
@ -196,39 +196,39 @@
{{-- Headers --}}
<section id="headers" data-scrollspy-target class="mb-12">
<h2 class="h3 mb-4 text-slate-800 dark:text-slate-100">Request Headers</h2>
<p class="text-slate-600 dark:text-slate-400 mb-4">
<h2 class="h3 mb-4 text-zinc-800 dark:text-zinc-100">Request Headers</h2>
<p class="text-zinc-600 dark:text-zinc-400 mb-4">
Every webhook request includes the following headers:
</p>
<div class="overflow-x-auto">
<table class="w-full text-sm">
<thead>
<tr class="border-b border-slate-200 dark:border-slate-700">
<th class="text-left py-3 px-4 font-medium text-slate-800 dark:text-slate-200">Header</th>
<th class="text-left py-3 px-4 font-medium text-slate-800 dark:text-slate-200">Description</th>
<tr class="border-b border-zinc-200 dark:border-zinc-700">
<th class="text-left py-3 px-4 font-medium text-zinc-800 dark:text-zinc-200">Header</th>
<th class="text-left py-3 px-4 font-medium text-zinc-800 dark:text-zinc-200">Description</th>
</tr>
</thead>
<tbody class="divide-y divide-slate-200 dark:divide-slate-700">
<tbody class="divide-y divide-zinc-200 dark:divide-zinc-700">
<tr>
<td class="py-3 px-4"><code class="px-1.5 py-0.5 bg-slate-100 dark:bg-slate-800 rounded text-xs">X-Webhook-Signature</code></td>
<td class="py-3 px-4 text-slate-600 dark:text-slate-400">HMAC-SHA256 signature for verification</td>
<td class="py-3 px-4"><code class="px-1.5 py-0.5 bg-zinc-100 dark:bg-zinc-800 rounded text-xs">X-Webhook-Signature</code></td>
<td class="py-3 px-4 text-zinc-600 dark:text-zinc-400">HMAC-SHA256 signature for verification</td>
</tr>
<tr>
<td class="py-3 px-4"><code class="px-1.5 py-0.5 bg-slate-100 dark:bg-slate-800 rounded text-xs">X-Webhook-Timestamp</code></td>
<td class="py-3 px-4 text-slate-600 dark:text-slate-400">Unix timestamp when the webhook was sent</td>
<td class="py-3 px-4"><code class="px-1.5 py-0.5 bg-zinc-100 dark:bg-zinc-800 rounded text-xs">X-Webhook-Timestamp</code></td>
<td class="py-3 px-4 text-zinc-600 dark:text-zinc-400">Unix timestamp when the webhook was sent</td>
</tr>
<tr>
<td class="py-3 px-4"><code class="px-1.5 py-0.5 bg-slate-100 dark:bg-slate-800 rounded text-xs">X-Webhook-Event</code></td>
<td class="py-3 px-4 text-slate-600 dark:text-slate-400">The event type (e.g., <code>bio.created</code>)</td>
<td class="py-3 px-4"><code class="px-1.5 py-0.5 bg-zinc-100 dark:bg-zinc-800 rounded text-xs">X-Webhook-Event</code></td>
<td class="py-3 px-4 text-zinc-600 dark:text-zinc-400">The event type (e.g., <code>bio.created</code>)</td>
</tr>
<tr>
<td class="py-3 px-4"><code class="px-1.5 py-0.5 bg-slate-100 dark:bg-slate-800 rounded text-xs">X-Webhook-Id</code></td>
<td class="py-3 px-4 text-slate-600 dark:text-slate-400">Unique delivery ID for idempotency</td>
<td class="py-3 px-4"><code class="px-1.5 py-0.5 bg-zinc-100 dark:bg-zinc-800 rounded text-xs">X-Webhook-Id</code></td>
<td class="py-3 px-4 text-zinc-600 dark:text-zinc-400">Unique delivery ID for idempotency</td>
</tr>
<tr>
<td class="py-3 px-4"><code class="px-1.5 py-0.5 bg-slate-100 dark:bg-slate-800 rounded text-xs">Content-Type</code></td>
<td class="py-3 px-4 text-slate-600 dark:text-slate-400">Always <code>application/json</code></td>
<td class="py-3 px-4"><code class="px-1.5 py-0.5 bg-zinc-100 dark:bg-zinc-800 rounded text-xs">Content-Type</code></td>
<td class="py-3 px-4 text-zinc-600 dark:text-zinc-400">Always <code>application/json</code></td>
</tr>
</tbody>
</table>
@ -237,13 +237,13 @@
{{-- Verification --}}
<section id="verification" data-scrollspy-target class="mb-12">
<h2 class="h3 mb-4 text-slate-800 dark:text-slate-100">Signature Verification</h2>
<p class="text-slate-600 dark:text-slate-400 mb-4">
<h2 class="h3 mb-4 text-zinc-800 dark:text-zinc-100">Signature Verification</h2>
<p class="text-zinc-600 dark:text-zinc-400 mb-4">
To verify a webhook signature, compute the HMAC-SHA256 of the timestamp concatenated with the raw request body using your webhook secret. The signature includes the timestamp to prevent replay attacks.
</p>
<h3 class="h4 mb-3 text-slate-800 dark:text-slate-100">Verification Algorithm</h3>
<ol class="list-decimal list-inside space-y-2 text-slate-600 dark:text-slate-400 mb-6">
<h3 class="h4 mb-3 text-zinc-800 dark:text-zinc-100">Verification Algorithm</h3>
<ol class="list-decimal list-inside space-y-2 text-zinc-600 dark:text-zinc-400 mb-6">
<li>Extract <code>X-Webhook-Signature</code> and <code>X-Webhook-Timestamp</code> headers</li>
<li>Concatenate: <code>timestamp + "." + raw_request_body</code></li>
<li>Compute: <code>HMAC-SHA256(concatenated_string, your_webhook_secret)</code></li>
@ -252,75 +252,75 @@
</ol>
{{-- PHP Example --}}
<h3 class="h4 mb-3 text-slate-800 dark:text-slate-100">PHP</h3>
<div class="bg-slate-800 rounded-sm overflow-hidden mb-6">
<div class="flex items-center justify-between px-4 py-2 border-b border-slate-700">
<span class="text-sm text-slate-400">webhook-handler.php</span>
<h3 class="h4 mb-3 text-zinc-800 dark:text-zinc-100">PHP</h3>
<div class="bg-zinc-800 rounded-sm overflow-hidden mb-6">
<div class="flex items-center justify-between px-4 py-2 border-b border-zinc-700">
<span class="text-sm text-zinc-400">webhook-handler.php</span>
</div>
<pre class="overflow-x-auto p-4 text-sm"><code class="font-pt-mono text-slate-300"><span class="text-purple-400">&lt;?php</span>
<pre class="overflow-x-auto p-4 text-sm"><code class="font-pt-mono text-zinc-300"><span class="text-purple-400">&lt;?php</span>
<span class="text-slate-500">// Get request data</span>
<span class="text-zinc-500">// Get request data</span>
<span class="text-purple-400">$payload</span> = <span class="text-teal-400">file_get_contents</span>(<span class="text-green-400">'php://input'</span>);
<span class="text-purple-400">$signature</span> = <span class="text-purple-400">$_SERVER</span>[<span class="text-green-400">'HTTP_X_WEBHOOK_SIGNATURE'</span>] ?? <span class="text-green-400">''</span>;
<span class="text-purple-400">$timestamp</span> = <span class="text-purple-400">$_SERVER</span>[<span class="text-green-400">'HTTP_X_WEBHOOK_TIMESTAMP'</span>] ?? <span class="text-green-400">''</span>;
<span class="text-purple-400">$secret</span> = <span class="text-teal-400">getenv</span>(<span class="text-green-400">'WEBHOOK_SECRET'</span>);
<span class="text-slate-500">// Verify timestamp (5 minute tolerance)</span>
<span class="text-zinc-500">// Verify timestamp (5 minute tolerance)</span>
<span class="text-purple-400">$tolerance</span> = <span class="text-amber-400">300</span>;
<span class="text-pink-400">if</span> (<span class="text-teal-400">abs</span>(<span class="text-teal-400">time</span>() - (<span class="text-pink-400">int</span>)<span class="text-purple-400">$timestamp</span>) > <span class="text-purple-400">$tolerance</span>) {
<span class="text-teal-400">http_response_code</span>(<span class="text-amber-400">401</span>);
<span class="text-pink-400">die</span>(<span class="text-green-400">'Webhook timestamp expired'</span>);
}
<span class="text-slate-500">// Compute expected signature</span>
<span class="text-zinc-500">// Compute expected signature</span>
<span class="text-purple-400">$signedPayload</span> = <span class="text-purple-400">$timestamp</span> . <span class="text-green-400">'.'</span> . <span class="text-purple-400">$payload</span>;
<span class="text-purple-400">$expectedSignature</span> = <span class="text-teal-400">hash_hmac</span>(<span class="text-green-400">'sha256'</span>, <span class="text-purple-400">$signedPayload</span>, <span class="text-purple-400">$secret</span>);
<span class="text-slate-500">// Verify signature (timing-safe comparison)</span>
<span class="text-zinc-500">// Verify signature (timing-safe comparison)</span>
<span class="text-pink-400">if</span> (!<span class="text-teal-400">hash_equals</span>(<span class="text-purple-400">$expectedSignature</span>, <span class="text-purple-400">$signature</span>)) {
<span class="text-teal-400">http_response_code</span>(<span class="text-amber-400">401</span>);
<span class="text-pink-400">die</span>(<span class="text-green-400">'Invalid webhook signature'</span>);
}
<span class="text-slate-500">// Signature valid - process the webhook</span>
<span class="text-zinc-500">// Signature valid - process the webhook</span>
<span class="text-purple-400">$event</span> = <span class="text-teal-400">json_decode</span>(<span class="text-purple-400">$payload</span>, <span class="text-pink-400">true</span>);
<span class="text-teal-400">processWebhook</span>(<span class="text-purple-400">$event</span>);</code></pre>
</div>
{{-- Node.js Example --}}
<h3 class="h4 mb-3 text-slate-800 dark:text-slate-100">Node.js</h3>
<div class="bg-slate-800 rounded-sm overflow-hidden mb-6">
<div class="flex items-center justify-between px-4 py-2 border-b border-slate-700">
<span class="text-sm text-slate-400">webhook-handler.js</span>
<h3 class="h4 mb-3 text-zinc-800 dark:text-zinc-100">Node.js</h3>
<div class="bg-zinc-800 rounded-sm overflow-hidden mb-6">
<div class="flex items-center justify-between px-4 py-2 border-b border-zinc-700">
<span class="text-sm text-zinc-400">webhook-handler.js</span>
</div>
<pre class="overflow-x-auto p-4 text-sm"><code class="font-pt-mono text-slate-300"><span class="text-pink-400">const</span> crypto = <span class="text-teal-400">require</span>(<span class="text-green-400">'crypto'</span>);
<pre class="overflow-x-auto p-4 text-sm"><code class="font-pt-mono text-zinc-300"><span class="text-pink-400">const</span> crypto = <span class="text-teal-400">require</span>(<span class="text-green-400">'crypto'</span>);
<span class="text-pink-400">const</span> express = <span class="text-teal-400">require</span>(<span class="text-green-400">'express'</span>);
<span class="text-pink-400">const</span> app = <span class="text-teal-400">express</span>();
app.<span class="text-teal-400">use</span>(express.<span class="text-teal-400">raw</span>({ type: <span class="text-green-400">'application/json'</span> }));
<span class="text-pink-400">const</span> WEBHOOK_SECRET = process.env.WEBHOOK_SECRET;
<span class="text-pink-400">const</span> TOLERANCE = <span class="text-amber-400">300</span>; <span class="text-slate-500">// 5 minutes</span>
<span class="text-pink-400">const</span> TOLERANCE = <span class="text-amber-400">300</span>; <span class="text-zinc-500">// 5 minutes</span>
app.<span class="text-teal-400">post</span>(<span class="text-green-400">'/webhook'</span>, (req, res) => {
<span class="text-pink-400">const</span> signature = req.headers[<span class="text-green-400">'x-webhook-signature'</span>];
<span class="text-pink-400">const</span> timestamp = req.headers[<span class="text-green-400">'x-webhook-timestamp'</span>];
<span class="text-pink-400">const</span> payload = req.body;
<span class="text-slate-500">// Verify timestamp</span>
<span class="text-zinc-500">// Verify timestamp</span>
<span class="text-pink-400">const</span> now = Math.<span class="text-teal-400">floor</span>(Date.<span class="text-teal-400">now</span>() / <span class="text-amber-400">1000</span>);
<span class="text-pink-400">if</span> (Math.<span class="text-teal-400">abs</span>(now - <span class="text-teal-400">parseInt</span>(timestamp)) > TOLERANCE) {
<span class="text-pink-400">return</span> res.<span class="text-teal-400">status</span>(<span class="text-amber-400">401</span>).<span class="text-teal-400">send</span>(<span class="text-green-400">'Webhook timestamp expired'</span>);
}
<span class="text-slate-500">// Compute expected signature</span>
<span class="text-zinc-500">// Compute expected signature</span>
<span class="text-pink-400">const</span> signedPayload = <span class="text-green-400">`${</span>timestamp<span class="text-green-400">}.${</span>payload<span class="text-green-400">}`</span>;
<span class="text-pink-400">const</span> expectedSignature = crypto
.<span class="text-teal-400">createHmac</span>(<span class="text-green-400">'sha256'</span>, WEBHOOK_SECRET)
.<span class="text-teal-400">update</span>(signedPayload)
.<span class="text-teal-400">digest</span>(<span class="text-green-400">'hex'</span>);
<span class="text-slate-500">// Verify signature (timing-safe comparison)</span>
<span class="text-zinc-500">// Verify signature (timing-safe comparison)</span>
<span class="text-pink-400">if</span> (!crypto.<span class="text-teal-400">timingSafeEqual</span>(
Buffer.<span class="text-teal-400">from</span>(expectedSignature),
Buffer.<span class="text-teal-400">from</span>(signature)
@ -328,7 +328,7 @@ app.<span class="text-teal-400">post</span>(<span class="text-green-400">'/webho
<span class="text-pink-400">return</span> res.<span class="text-teal-400">status</span>(<span class="text-amber-400">401</span>).<span class="text-teal-400">send</span>(<span class="text-green-400">'Invalid webhook signature'</span>);
}
<span class="text-slate-500">// Signature valid - process the webhook</span>
<span class="text-zinc-500">// Signature valid - process the webhook</span>
<span class="text-pink-400">const</span> event = JSON.<span class="text-teal-400">parse</span>(payload);
<span class="text-teal-400">processWebhook</span>(event);
res.<span class="text-teal-400">status</span>(<span class="text-amber-400">200</span>).<span class="text-teal-400">send</span>(<span class="text-green-400">'OK'</span>);
@ -336,12 +336,12 @@ app.<span class="text-teal-400">post</span>(<span class="text-green-400">'/webho
</div>
{{-- Python Example --}}
<h3 class="h4 mb-3 text-slate-800 dark:text-slate-100">Python</h3>
<div class="bg-slate-800 rounded-sm overflow-hidden mb-6">
<div class="flex items-center justify-between px-4 py-2 border-b border-slate-700">
<span class="text-sm text-slate-400">webhook_handler.py</span>
<h3 class="h4 mb-3 text-zinc-800 dark:text-zinc-100">Python</h3>
<div class="bg-zinc-800 rounded-sm overflow-hidden mb-6">
<div class="flex items-center justify-between px-4 py-2 border-b border-zinc-700">
<span class="text-sm text-zinc-400">webhook_handler.py</span>
</div>
<pre class="overflow-x-auto p-4 text-sm"><code class="font-pt-mono text-slate-300"><span class="text-pink-400">import</span> hmac
<pre class="overflow-x-auto p-4 text-sm"><code class="font-pt-mono text-zinc-300"><span class="text-pink-400">import</span> hmac
<span class="text-pink-400">import</span> hashlib
<span class="text-pink-400">import</span> time
<span class="text-pink-400">import</span> os
@ -349,7 +349,7 @@ app.<span class="text-teal-400">post</span>(<span class="text-green-400">'/webho
app = Flask(__name__)
WEBHOOK_SECRET = os.environ[<span class="text-green-400">'WEBHOOK_SECRET'</span>]
TOLERANCE = <span class="text-amber-400">300</span> <span class="text-slate-500"># 5 minutes</span>
TOLERANCE = <span class="text-amber-400">300</span> <span class="text-zinc-500"># 5 minutes</span>
<span class="text-pink-400">@</span>app.route(<span class="text-green-400">'/webhook'</span>, methods=[<span class="text-green-400">'POST'</span>])
<span class="text-pink-400">def</span> <span class="text-teal-400">webhook</span>():
@ -357,11 +357,11 @@ TOLERANCE = <span class="text-amber-400">300</span> <span class="text-slate-500
timestamp = request.headers.get(<span class="text-green-400">'X-Webhook-Timestamp'</span>, <span class="text-green-400">''</span>)
payload = request.get_data(as_text=<span class="text-pink-400">True</span>)
<span class="text-slate-500"># Verify timestamp</span>
<span class="text-zinc-500"># Verify timestamp</span>
<span class="text-pink-400">if</span> abs(time.time() - int(timestamp)) > TOLERANCE:
abort(<span class="text-amber-400">401</span>, <span class="text-green-400">'Webhook timestamp expired'</span>)
<span class="text-slate-500"># Compute expected signature</span>
<span class="text-zinc-500"># Compute expected signature</span>
signed_payload = <span class="text-green-400">f'{timestamp}.{payload}'</span>
expected_signature = hmac.new(
WEBHOOK_SECRET.encode(),
@ -369,40 +369,40 @@ TOLERANCE = <span class="text-amber-400">300</span> <span class="text-slate-500
hashlib.sha256
).hexdigest()
<span class="text-slate-500"># Verify signature (timing-safe comparison)</span>
<span class="text-zinc-500"># Verify signature (timing-safe comparison)</span>
<span class="text-pink-400">if not</span> hmac.compare_digest(expected_signature, signature):
abort(<span class="text-amber-400">401</span>, <span class="text-green-400">'Invalid webhook signature'</span>)
<span class="text-slate-500"># Signature valid - process the webhook</span>
<span class="text-zinc-500"># Signature valid - process the webhook</span>
event = request.get_json()
process_webhook(event)
<span class="text-pink-400">return</span> <span class="text-green-400">'OK'</span>, <span class="text-amber-400">200</span></code></pre>
</div>
{{-- Ruby Example --}}
<h3 class="h4 mb-3 text-slate-800 dark:text-slate-100">Ruby</h3>
<div class="bg-slate-800 rounded-sm overflow-hidden mb-6">
<div class="flex items-center justify-between px-4 py-2 border-b border-slate-700">
<span class="text-sm text-slate-400">webhook_handler.rb</span>
<h3 class="h4 mb-3 text-zinc-800 dark:text-zinc-100">Ruby</h3>
<div class="bg-zinc-800 rounded-sm overflow-hidden mb-6">
<div class="flex items-center justify-between px-4 py-2 border-b border-zinc-700">
<span class="text-sm text-zinc-400">webhook_handler.rb</span>
</div>
<pre class="overflow-x-auto p-4 text-sm"><code class="font-pt-mono text-slate-300"><span class="text-pink-400">require</span> <span class="text-green-400">'sinatra'</span>
<pre class="overflow-x-auto p-4 text-sm"><code class="font-pt-mono text-zinc-300"><span class="text-pink-400">require</span> <span class="text-green-400">'sinatra'</span>
<span class="text-pink-400">require</span> <span class="text-green-400">'openssl'</span>
<span class="text-pink-400">require</span> <span class="text-green-400">'json'</span>
WEBHOOK_SECRET = ENV[<span class="text-green-400">'WEBHOOK_SECRET'</span>]
TOLERANCE = <span class="text-amber-400">300</span> <span class="text-slate-500"># 5 minutes</span>
TOLERANCE = <span class="text-amber-400">300</span> <span class="text-zinc-500"># 5 minutes</span>
post <span class="text-green-400">'/webhook'</span> <span class="text-pink-400">do</span>
signature = request.env[<span class="text-green-400">'HTTP_X_WEBHOOK_SIGNATURE'</span>] || <span class="text-green-400">''</span>
timestamp = request.env[<span class="text-green-400">'HTTP_X_WEBHOOK_TIMESTAMP'</span>] || <span class="text-green-400">''</span>
payload = request.body.read
<span class="text-slate-500"># Verify timestamp</span>
<span class="text-zinc-500"># Verify timestamp</span>
<span class="text-pink-400">if</span> (Time.now.to_i - timestamp.to_i).<span class="text-teal-400">abs</span> > TOLERANCE
halt <span class="text-amber-400">401</span>, <span class="text-green-400">'Webhook timestamp expired'</span>
<span class="text-pink-400">end</span>
<span class="text-slate-500"># Compute expected signature</span>
<span class="text-zinc-500"># Compute expected signature</span>
signed_payload = <span class="text-green-400">"#{timestamp}.#{payload}"</span>
expected_signature = OpenSSL::HMAC.hexdigest(
<span class="text-green-400">'sha256'</span>,
@ -410,12 +410,12 @@ post <span class="text-green-400">'/webhook'</span> <span class="text-pink-400">
signed_payload
)
<span class="text-slate-500"># Verify signature (timing-safe comparison)</span>
<span class="text-zinc-500"># Verify signature (timing-safe comparison)</span>
<span class="text-pink-400">unless</span> Rack::Utils.secure_compare(expected_signature, signature)
halt <span class="text-amber-400">401</span>, <span class="text-green-400">'Invalid webhook signature'</span>
<span class="text-pink-400">end</span>
<span class="text-slate-500"># Signature valid - process the webhook</span>
<span class="text-zinc-500"># Signature valid - process the webhook</span>
event = JSON.parse(payload)
process_webhook(event)
<span class="text-amber-400">200</span>
@ -423,12 +423,12 @@ post <span class="text-green-400">'/webhook'</span> <span class="text-pink-400">
</div>
{{-- Go Example --}}
<h3 class="h4 mb-3 text-slate-800 dark:text-slate-100">Go</h3>
<div class="bg-slate-800 rounded-sm overflow-hidden">
<div class="flex items-center justify-between px-4 py-2 border-b border-slate-700">
<span class="text-sm text-slate-400">webhook_handler.go</span>
<h3 class="h4 mb-3 text-zinc-800 dark:text-zinc-100">Go</h3>
<div class="bg-zinc-800 rounded-sm overflow-hidden">
<div class="flex items-center justify-between px-4 py-2 border-b border-zinc-700">
<span class="text-sm text-zinc-400">webhook_handler.go</span>
</div>
<pre class="overflow-x-auto p-4 text-sm"><code class="font-pt-mono text-slate-300"><span class="text-pink-400">package</span> main
<pre class="overflow-x-auto p-4 text-sm"><code class="font-pt-mono text-zinc-300"><span class="text-pink-400">package</span> main
<span class="text-pink-400">import</span> (
<span class="text-green-400">"crypto/hmac"</span>
@ -443,7 +443,7 @@ post <span class="text-green-400">'/webhook'</span> <span class="text-pink-400">
<span class="text-green-400">"time"</span>
)
<span class="text-pink-400">const</span> tolerance = <span class="text-amber-400">300</span> <span class="text-slate-500">// 5 minutes</span>
<span class="text-pink-400">const</span> tolerance = <span class="text-amber-400">300</span> <span class="text-zinc-500">// 5 minutes</span>
<span class="text-pink-400">func</span> <span class="text-teal-400">webhookHandler</span>(w http.ResponseWriter, r *http.Request) {
signature := r.Header.Get(<span class="text-green-400">"X-Webhook-Signature"</span>)
@ -452,20 +452,20 @@ post <span class="text-green-400">'/webhook'</span> <span class="text-pink-400">
payload, _ := io.ReadAll(r.Body)
<span class="text-slate-500">// Verify timestamp</span>
<span class="text-zinc-500">// Verify timestamp</span>
ts, _ := strconv.ParseInt(timestamp, <span class="text-amber-400">10</span>, <span class="text-amber-400">64</span>)
<span class="text-pink-400">if</span> math.Abs(<span class="text-teal-400">float64</span>(time.Now().Unix()-ts)) > tolerance {
http.Error(w, <span class="text-green-400">"Webhook timestamp expired"</span>, <span class="text-amber-400">401</span>)
<span class="text-pink-400">return</span>
}
<span class="text-slate-500">// Compute expected signature</span>
<span class="text-zinc-500">// Compute expected signature</span>
signedPayload := timestamp + <span class="text-green-400">"."</span> + <span class="text-teal-400">string</span>(payload)
mac := hmac.New(sha256.New, []<span class="text-teal-400">byte</span>(secret))
mac.Write([]<span class="text-teal-400">byte</span>(signedPayload))
expectedSignature := hex.EncodeToString(mac.Sum(<span class="text-pink-400">nil</span>))
<span class="text-slate-500">// Verify signature (timing-safe comparison)</span>
<span class="text-zinc-500">// Verify signature (timing-safe comparison)</span>
<span class="text-pink-400">if</span> subtle.ConstantTimeCompare(
[]<span class="text-teal-400">byte</span>(expectedSignature),
[]<span class="text-teal-400">byte</span>(signature),
@ -474,7 +474,7 @@ post <span class="text-green-400">'/webhook'</span> <span class="text-pink-400">
<span class="text-pink-400">return</span>
}
<span class="text-slate-500">// Signature valid - process the webhook</span>
<span class="text-zinc-500">// Signature valid - process the webhook</span>
processWebhook(payload)
w.WriteHeader(http.StatusOK)
}</code></pre>
@ -483,53 +483,53 @@ post <span class="text-green-400">'/webhook'</span> <span class="text-pink-400">
{{-- Retry Policy --}}
<section id="retry-policy" data-scrollspy-target class="mb-12">
<h2 class="h3 mb-4 text-slate-800 dark:text-slate-100">Retry Policy</h2>
<p class="text-slate-600 dark:text-slate-400 mb-4">
<h2 class="h3 mb-4 text-zinc-800 dark:text-zinc-100">Retry Policy</h2>
<p class="text-zinc-600 dark:text-zinc-400 mb-4">
If your endpoint returns a non-2xx status code or times out, we'll retry with exponential backoff:
</p>
<div class="overflow-x-auto mb-4">
<table class="w-full text-sm">
<thead>
<tr class="border-b border-slate-200 dark:border-slate-700">
<th class="text-left py-3 px-4 font-medium text-slate-800 dark:text-slate-200">Attempt</th>
<th class="text-left py-3 px-4 font-medium text-slate-800 dark:text-slate-200">Delay</th>
<tr class="border-b border-zinc-200 dark:border-zinc-700">
<th class="text-left py-3 px-4 font-medium text-zinc-800 dark:text-zinc-200">Attempt</th>
<th class="text-left py-3 px-4 font-medium text-zinc-800 dark:text-zinc-200">Delay</th>
</tr>
</thead>
<tbody class="divide-y divide-slate-200 dark:divide-slate-700">
<tbody class="divide-y divide-zinc-200 dark:divide-zinc-700">
<tr>
<td class="py-3 px-4 text-slate-600 dark:text-slate-400">1 (initial)</td>
<td class="py-3 px-4 text-slate-600 dark:text-slate-400">Immediate</td>
<td class="py-3 px-4 text-zinc-600 dark:text-zinc-400">1 (initial)</td>
<td class="py-3 px-4 text-zinc-600 dark:text-zinc-400">Immediate</td>
</tr>
<tr>
<td class="py-3 px-4 text-slate-600 dark:text-slate-400">2</td>
<td class="py-3 px-4 text-slate-600 dark:text-slate-400">1 minute</td>
<td class="py-3 px-4 text-zinc-600 dark:text-zinc-400">2</td>
<td class="py-3 px-4 text-zinc-600 dark:text-zinc-400">1 minute</td>
</tr>
<tr>
<td class="py-3 px-4 text-slate-600 dark:text-slate-400">3</td>
<td class="py-3 px-4 text-slate-600 dark:text-slate-400">5 minutes</td>
<td class="py-3 px-4 text-zinc-600 dark:text-zinc-400">3</td>
<td class="py-3 px-4 text-zinc-600 dark:text-zinc-400">5 minutes</td>
</tr>
<tr>
<td class="py-3 px-4 text-slate-600 dark:text-slate-400">4</td>
<td class="py-3 px-4 text-slate-600 dark:text-slate-400">30 minutes</td>
<td class="py-3 px-4 text-zinc-600 dark:text-zinc-400">4</td>
<td class="py-3 px-4 text-zinc-600 dark:text-zinc-400">30 minutes</td>
</tr>
<tr>
<td class="py-3 px-4 text-slate-600 dark:text-slate-400">5 (final)</td>
<td class="py-3 px-4 text-slate-600 dark:text-slate-400">2 hours</td>
<td class="py-3 px-4 text-zinc-600 dark:text-zinc-400">5 (final)</td>
<td class="py-3 px-4 text-zinc-600 dark:text-zinc-400">2 hours</td>
</tr>
</tbody>
</table>
</div>
<p class="text-slate-600 dark:text-slate-400">
<p class="text-zinc-600 dark:text-zinc-400">
After 5 failed attempts, the delivery is marked as failed. If your endpoint fails 10 consecutive deliveries, it will be automatically disabled. You can re-enable it from your webhook settings.
</p>
</section>
{{-- Best Practices --}}
<section id="best-practices" data-scrollspy-target class="mb-12">
<h2 class="h3 mb-4 text-slate-800 dark:text-slate-100">Best Practices</h2>
<ul class="space-y-3 text-slate-600 dark:text-slate-400">
<h2 class="h3 mb-4 text-zinc-800 dark:text-zinc-100">Best Practices</h2>
<ul class="space-y-3 text-zinc-600 dark:text-zinc-400">
<li class="flex items-start">
<svg class="fill-green-500 shrink-0 mr-3 mt-1" width="16" height="16" viewBox="0 0 16 16">
<path d="M8 0a8 8 0 1 0 0 16A8 8 0 0 0 8 0zm3.78 5.22a.75.75 0 0 1 0 1.06l-4.5 4.5a.75.75 0 0 1-1.06 0l-2-2a.75.75 0 1 1 1.06-1.06L6.75 9.19l3.97-3.97a.75.75 0 0 1 1.06 0z"/>
@ -570,11 +570,11 @@ post <span class="text-green-400">'/webhook'</span> <span class="text-pink-400">
</section>
{{-- Next steps --}}
<div class="flex items-center justify-between pt-8 border-t border-slate-200 dark:border-slate-700">
<a href="{{ route('api.guides.qrcodes') }}" class="text-slate-600 hover:text-slate-900 dark:text-slate-400 dark:hover:text-slate-200">
<div class="flex items-center justify-between pt-8 border-t border-zinc-200 dark:border-zinc-700">
<a href="{{ route('api.guides.qrcodes') }}" class="text-zinc-600 hover:text-zinc-900 dark:text-zinc-400 dark:hover:text-zinc-200">
&larr; QR Code Generation
</a>
<a href="{{ route('api.guides.errors') }}" class="text-blue-600 hover:text-blue-700 dark:hover:text-blue-500 font-medium">
<a href="{{ route('api.guides.errors') }}" class="text-cyan-600 hover:text-cyan-700 dark:hover:text-cyan-500 font-medium">
Error Handling &rarr;
</a>
</div>

View file

@ -1,7 +1,7 @@
@extends('api::layouts.docs')
@section('title', 'API Documentation')
@section('description', 'Build powerful integrations with the Host UK API. Access biolinks, workspaces, QR codes, and more.')
@section('description', 'Build powerful integrations with the API. Access brain memory, content scoring, and more.')
@section('content')
<div class="max-w-7xl mx-auto px-4 sm:px-6 py-12 md:py-20">
@ -9,18 +9,18 @@
{{-- Hero --}}
<div class="max-w-3xl mx-auto text-center mb-16">
<div class="mb-4">
<span class="font-nycd text-xl text-blue-600">Developer Documentation</span>
<span class="font-nycd text-xl text-cyan-600">Developer Documentation</span>
</div>
<h1 class="h1 mb-6 text-slate-800 dark:text-slate-100">Build with the Host UK API</h1>
<p class="text-xl text-slate-600 dark:text-slate-400 mb-8">
Integrate biolinks, workspaces, QR codes, and analytics into your applications.
Full REST API with comprehensive documentation and SDK support.
<h1 class="h1 mb-6 text-zinc-800 dark:text-zinc-100">Build with the API</h1>
<p class="text-xl text-zinc-600 dark:text-zinc-400 mb-8">
Store and retrieve agent memories, score content for AI patterns,
and integrate intelligent tooling into your applications.
</p>
<div class="flex flex-wrap justify-center gap-4">
<a href="{{ route('api.guides.quickstart') }}" class="btn text-white bg-blue-600 hover:bg-blue-700 px-6 py-3 rounded-sm font-medium">
<a href="{{ route('api.guides.quickstart') }}" class="btn text-white bg-cyan-600 hover:bg-cyan-700 px-6 py-3 rounded-sm font-medium">
Get Started
</a>
<a href="{{ route('api.reference') }}" class="btn text-slate-600 bg-white border border-slate-200 hover:border-slate-300 dark:text-slate-300 dark:bg-slate-800 dark:border-slate-700 dark:hover:border-slate-600 px-6 py-3 rounded-sm font-medium">
<a href="{{ route('api.reference') }}" class="btn text-zinc-600 bg-white border border-zinc-200 hover:border-zinc-300 dark:text-zinc-300 dark:bg-zinc-800 dark:border-zinc-700 dark:hover:border-zinc-600 px-6 py-3 rounded-sm font-medium">
API Reference
</a>
</div>
@ -29,50 +29,44 @@
{{-- Features grid --}}
<div class="grid md:grid-cols-3 gap-8 mb-16">
{{-- Authentication --}}
<div class="bg-slate-50 dark:bg-slate-800 border border-slate-200 dark:border-slate-700 rounded-sm p-6">
<div class="w-10 h-10 flex items-center justify-center bg-blue-100 dark:bg-blue-900/30 rounded-sm mb-4">
<svg class="w-5 h-5 fill-blue-600" viewBox="0 0 20 20">
<path d="M10 2a5 5 0 0 0-5 5v2a2 2 0 0 0-2 2v5a2 2 0 0 0 2 2h10a2 2 0 0 0 2-2v-5a2 2 0 0 0-2-2V7a5 5 0 0 0-5-5zm3 7V7a3 3 0 1 0-6 0v2h6z"/>
</svg>
{{-- Brain Memory --}}
<div class="bg-zinc-50 dark:bg-zinc-800 border border-zinc-200 dark:border-zinc-700 rounded-sm p-6">
<div class="w-10 h-10 flex items-center justify-center bg-cyan-100 dark:bg-cyan-900/30 rounded-sm mb-4">
<i class="fa-solid fa-brain text-cyan-600"></i>
</div>
<h3 class="h4 mb-2 text-slate-800 dark:text-slate-100">Authentication</h3>
<p class="text-slate-600 dark:text-slate-400 mb-4">
<h3 class="h4 mb-2 text-zinc-800 dark:text-zinc-100">Brain Memory</h3>
<p class="text-zinc-600 dark:text-zinc-400 mb-4">
Store and retrieve agent memories with vector search. Powered by Qdrant for semantic retrieval.
</p>
<a href="{{ route('api.reference') }}#brain" class="text-cyan-600 hover:text-cyan-700 dark:hover:text-cyan-500 text-sm font-medium">
Learn more &rarr;
</a>
</div>
{{-- Content Scoring --}}
<div class="bg-zinc-50 dark:bg-zinc-800 border border-zinc-200 dark:border-zinc-700 rounded-sm p-6">
<div class="w-10 h-10 flex items-center justify-center bg-purple-100 dark:bg-purple-900/30 rounded-sm mb-4">
<i class="fa-solid fa-chart-line text-purple-600"></i>
</div>
<h3 class="h4 mb-2 text-zinc-800 dark:text-zinc-100">Content Scoring</h3>
<p class="text-zinc-600 dark:text-zinc-400 mb-4">
Score text for AI-generated patterns and analyse linguistic imprints via the EaaS scoring engine.
</p>
<a href="{{ route('api.reference') }}#score" class="text-cyan-600 hover:text-cyan-700 dark:hover:text-cyan-500 text-sm font-medium">
Learn more &rarr;
</a>
</div>
{{-- Authentication --}}
<div class="bg-zinc-50 dark:bg-zinc-800 border border-zinc-200 dark:border-zinc-700 rounded-sm p-6">
<div class="w-10 h-10 flex items-center justify-center bg-amber-100 dark:bg-amber-900/30 rounded-sm mb-4">
<i class="fa-solid fa-key text-amber-600"></i>
</div>
<h3 class="h4 mb-2 text-zinc-800 dark:text-zinc-100">Authentication</h3>
<p class="text-zinc-600 dark:text-zinc-400 mb-4">
Secure API key authentication with scoped permissions. Generate keys from your workspace settings.
</p>
<a href="{{ route('api.guides.authentication') }}" class="text-blue-600 hover:text-blue-700 dark:hover:text-blue-500 text-sm font-medium">
Learn more &rarr;
</a>
</div>
{{-- Biolinks --}}
<div class="bg-slate-50 dark:bg-slate-800 border border-slate-200 dark:border-slate-700 rounded-sm p-6">
<div class="w-10 h-10 flex items-center justify-center bg-purple-100 dark:bg-purple-900/30 rounded-sm mb-4">
<svg class="w-5 h-5 fill-purple-600" viewBox="0 0 20 20">
<path d="M12.586 4.586a2 2 0 1 1 2.828 2.828l-3 3a2 2 0 0 1-2.828 0 1 1 0 0 0-1.414 1.414 4 4 0 0 0 5.656 0l3-3a4 4 0 0 0-5.656-5.656l-1.5 1.5a1 1 0 1 0 1.414 1.414l1.5-1.5zm-5 5a2 2 0 0 1 2.828 0 1 1 0 1 0 1.414-1.414 4 4 0 0 0-5.656 0l-3 3a4 4 0 1 0 5.656 5.656l1.5-1.5a1 1 0 1 0-1.414-1.414l-1.5 1.5a2 2 0 1 1-2.828-2.828l3-3z"/>
</svg>
</div>
<h3 class="h4 mb-2 text-slate-800 dark:text-slate-100">Biolinks</h3>
<p class="text-slate-600 dark:text-slate-400 mb-4">
Create, update, and manage biolink pages with blocks, themes, and analytics programmatically.
</p>
<a href="{{ route('api.guides.biolinks') }}" class="text-blue-600 hover:text-blue-700 dark:hover:text-blue-500 text-sm font-medium">
Learn more &rarr;
</a>
</div>
{{-- QR Codes --}}
<div class="bg-slate-50 dark:bg-slate-800 border border-slate-200 dark:border-slate-700 rounded-sm p-6">
<div class="w-10 h-10 flex items-center justify-center bg-teal-100 dark:bg-teal-900/30 rounded-sm mb-4">
<svg class="w-5 h-5 fill-teal-600" viewBox="0 0 20 20">
<path fill-rule="evenodd" d="M3 4a1 1 0 0 1 1-1h3a1 1 0 0 1 1 1v3a1 1 0 0 1-1 1H4a1 1 0 0 1-1-1V4zm2 2V5h1v1H5zm-2 7a1 1 0 0 1 1-1h3a1 1 0 0 1 1 1v3a1 1 0 0 1-1 1H4a1 1 0 0 1-1-1v-3zm2 2v-1h1v1H5zm7-13a1 1 0 0 0-1 1v3a1 1 0 0 0 1 1h3a1 1 0 0 0 1-1V4a1 1 0 0 0-1-1h-3zm1 2v1h1V5h-1z"/>
</svg>
</div>
<h3 class="h4 mb-2 text-slate-800 dark:text-slate-100">QR Codes</h3>
<p class="text-slate-600 dark:text-slate-400 mb-4">
Generate customisable QR codes with colours, logos, and multiple formats for any URL.
</p>
<a href="{{ route('api.guides.qrcodes') }}" class="text-blue-600 hover:text-blue-700 dark:hover:text-blue-500 text-sm font-medium">
<a href="{{ route('api.guides.authentication') }}" class="text-cyan-600 hover:text-cyan-700 dark:hover:text-cyan-500 text-sm font-medium">
Learn more &rarr;
</a>
</div>
@ -81,21 +75,23 @@
{{-- Quick start code example --}}
<div class="max-w-4xl mx-auto">
<h2 class="h3 mb-6 text-center text-slate-800 dark:text-slate-100">Quick Start</h2>
<div class="bg-slate-800 rounded-sm overflow-hidden">
<div class="flex items-center justify-between px-4 py-2 border-b border-slate-700">
<span class="text-sm text-slate-400">cURL</span>
<button class="text-xs text-slate-500 hover:text-slate-300" onclick="navigator.clipboard.writeText(this.closest('.bg-slate-800').querySelector('code').textContent)">
<h2 class="h3 mb-6 text-center text-zinc-800 dark:text-zinc-100">Quick Start</h2>
<div class="bg-zinc-800 rounded-sm overflow-hidden">
<div class="flex items-center justify-between px-4 py-2 border-b border-zinc-700">
<span class="text-sm text-zinc-400">cURL</span>
<button class="text-xs text-zinc-500 hover:text-zinc-300" onclick="navigator.clipboard.writeText(this.closest('.bg-zinc-800').querySelector('code').textContent)">
Copy
</button>
</div>
<pre class="overflow-x-auto p-4 text-sm"><code class="font-pt-mono text-slate-300"><span class="text-teal-400">curl</span> <span class="text-slate-500">--request</span> GET \
<span class="text-slate-500">--url</span> <span class="text-amber-400">'https://api.host.uk.com/api/v1/bio'</span> \
<span class="text-slate-500">--header</span> <span class="text-amber-400">'Authorization: Bearer hk_your_api_key'</span></code></pre>
<pre class="overflow-x-auto p-4 text-sm"><code class="font-pt-mono text-zinc-300"><span class="text-teal-400">curl</span> <span class="text-zinc-500">--request</span> POST \
<span class="text-zinc-500">--url</span> <span class="text-amber-400">'https://api.lthn.ai/v1/brain/recall'</span> \
<span class="text-zinc-500">--header</span> <span class="text-amber-400">'Authorization: Bearer hk_your_api_key'</span> \
<span class="text-zinc-500">--header</span> <span class="text-amber-400">'Content-Type: application/json'</span> \
<span class="text-zinc-500">--data</span> <span class="text-amber-400">'{"query": "hello world"}'</span></code></pre>
</div>
<div class="mt-4 text-center">
<a href="{{ route('api.guides.quickstart') }}" class="text-blue-600 hover:text-blue-700 dark:hover:text-blue-500 text-sm font-medium">
<a href="{{ route('api.guides.quickstart') }}" class="text-cyan-600 hover:text-cyan-700 dark:hover:text-cyan-500 text-sm font-medium">
View full quick start guide &rarr;
</a>
</div>
@ -103,30 +99,30 @@
{{-- API endpoints preview --}}
<div class="mt-16">
<h2 class="h3 mb-8 text-center text-slate-800 dark:text-slate-100">API Endpoints</h2>
<h2 class="h3 mb-8 text-center text-zinc-800 dark:text-zinc-100">API Endpoints</h2>
<div class="grid md:grid-cols-2 gap-4 max-w-4xl mx-auto">
@foreach([
['method' => 'GET', 'path' => '/api/v1/workspaces', 'desc' => 'List all workspaces'],
['method' => 'GET', 'path' => '/api/v1/bio', 'desc' => 'List all biolinks'],
['method' => 'POST', 'path' => '/api/v1/bio', 'desc' => 'Create a biolink'],
['method' => 'GET', 'path' => '/api/v1/bio/{id}/qr', 'desc' => 'Generate QR code'],
['method' => 'GET', 'path' => '/api/v1/shortlinks', 'desc' => 'List short links'],
['method' => 'POST', 'path' => '/api/v1/qr/generate', 'desc' => 'Generate QR for any URL'],
['method' => 'POST', 'path' => '/v1/brain/remember', 'desc' => 'Store a memory'],
['method' => 'POST', 'path' => '/v1/brain/recall', 'desc' => 'Search memories by query'],
['method' => 'DELETE', 'path' => '/v1/brain/forget/{id}', 'desc' => 'Delete a memory'],
['method' => 'POST', 'path' => '/v1/score/content', 'desc' => 'Score text for AI patterns'],
['method' => 'POST', 'path' => '/v1/score/imprint', 'desc' => 'Linguistic imprint analysis'],
['method' => 'GET', 'path' => '/v1/score/health', 'desc' => 'Scoring engine health check'],
] as $endpoint)
<a href="{{ route('api.reference') }}" class="flex items-center gap-4 p-4 bg-slate-50 dark:bg-slate-800 border border-slate-200 dark:border-slate-700 rounded-sm hover:border-blue-300 dark:hover:border-blue-600 transition-colors">
<span class="inline-flex items-center justify-center px-2 py-1 text-xs font-medium rounded {{ $endpoint['method'] === 'GET' ? 'bg-blue-100 text-blue-700 dark:bg-blue-900/30 dark:text-blue-400' : 'bg-green-100 text-green-700 dark:bg-green-900/30 dark:text-green-400' }}">
<a href="{{ route('api.reference') }}" class="flex items-center gap-4 p-4 bg-zinc-50 dark:bg-zinc-800 border border-zinc-200 dark:border-zinc-700 rounded-sm hover:border-cyan-300 dark:hover:border-cyan-600 transition-colors">
<span class="inline-flex items-center justify-center px-2 py-1 text-xs font-medium rounded {{ $endpoint['method'] === 'GET' ? 'bg-cyan-100 text-cyan-700 dark:bg-cyan-900/30 dark:text-cyan-400' : ($endpoint['method'] === 'DELETE' ? 'bg-red-100 text-red-700 dark:bg-red-900/30 dark:text-red-400' : 'bg-green-100 text-green-700 dark:bg-green-900/30 dark:text-green-400') }}">
{{ $endpoint['method'] }}
</span>
<div class="flex-1 min-w-0">
<code class="text-sm font-pt-mono text-slate-800 dark:text-slate-200 truncate block">{{ $endpoint['path'] }}</code>
<span class="text-xs text-slate-500 dark:text-slate-400">{{ $endpoint['desc'] }}</span>
<code class="text-sm font-pt-mono text-zinc-800 dark:text-zinc-200 truncate block">{{ $endpoint['path'] }}</code>
<span class="text-xs text-zinc-500 dark:text-zinc-400">{{ $endpoint['desc'] }}</span>
</div>
</a>
@endforeach
</div>
<div class="mt-8 text-center">
<a href="{{ route('api.swagger') }}" class="text-blue-600 hover:text-blue-700 dark:hover:text-blue-500 font-medium">
<a href="{{ route('api.swagger') }}" class="text-cyan-600 hover:text-cyan-700 dark:hover:text-cyan-500 font-medium">
View all endpoints in Swagger UI &rarr;
</a>
</div>

View file

@ -1,19 +1,14 @@
@php
$appName = config('core.app.name', 'Core PHP');
@endphp
<!DOCTYPE html>
<html lang="en" class="scroll-smooth">
<head>
<meta charset="utf-8">
<title>@yield('title', 'API Documentation') - Host UK</title>
<title>@yield('title', 'API Documentation') - {{ $appName }}</title>
<meta name="viewport" content="width=device-width,initial-scale=1">
<meta name="csrf-token" content="{{ csrf_token() }}">
<meta name="description" content="@yield('description', 'Host UK API documentation, guides, and reference.')">
<!-- Respect user's dark mode preference before page renders -->
<script>
if (localStorage.getItem('flux.appearance') === 'dark' ||
(!localStorage.getItem('flux.appearance') && window.matchMedia('(prefers-color-scheme: dark)').matches)) {
document.documentElement.classList.add('dark');
}
</script>
<meta name="description" content="@yield('description', $appName . ' REST API documentation, guides, and reference.')">
<!-- Fonts -->
@include('layouts::partials.fonts')
@ -21,22 +16,19 @@
<!-- Font Awesome Pro -->
<link rel="stylesheet" href="{{ \Core\Helpers\Cdn::versioned('vendor/fontawesome/css/all.min.css') }}">
<!-- Tailwind / Vite -->
<!-- Tailwind / Vite + Flux -->
@vite(['resources/css/app.css', 'resources/js/app.js'])
<!-- Alpine.js -->
<script defer src="https://cdn.jsdelivr.net/npm/alpinejs@3.x.x/dist/cdn.min.js"></script>
<style>[x-cloak] { display: none !important; }</style>
@fluxAppearance
@stack('head')
</head>
<body class="font-sans antialiased bg-white text-slate-800 dark:bg-slate-900 dark:text-slate-200">
<body class="font-sans antialiased bg-white text-zinc-800 dark:bg-zinc-900 dark:text-zinc-200">
<div class="flex flex-col min-h-screen overflow-hidden">
{{-- Site header --}}
<header class="fixed w-full z-30">
<div class="absolute inset-0 bg-white/70 border-b border-slate-200 backdrop-blur-sm -z-10 dark:bg-slate-900/70 dark:border-slate-800" aria-hidden="true"></div>
<div class="absolute inset-0 bg-white/70 border-b border-zinc-200 backdrop-blur-sm -z-10 dark:bg-zinc-900/70 dark:border-zinc-800" aria-hidden="true"></div>
<div class="max-w-7xl mx-auto px-4 sm:px-6">
<div class="flex items-center justify-between h-16 md:h-20">
@ -45,33 +37,33 @@
<div class="flex items-center gap-4 md:gap-8">
{{-- Logo --}}
<a href="{{ route('api.docs') }}" class="flex items-center gap-2">
<svg class="w-8 h-8 text-blue-600" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor">
<svg class="w-8 h-8 text-cyan-600" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" d="M17.25 6.75 22.5 12l-5.25 5.25m-10.5 0L1.5 12l5.25-5.25m7.5-3-4.5 16.5" />
</svg>
<span class="font-semibold text-slate-800 dark:text-slate-200">Host UK API</span>
<span class="font-semibold text-zinc-800 dark:text-zinc-200">{{ $appName }} API</span>
</a>
{{-- Search --}}
<div class="grow" x-data="{ searchOpen: false }">
<button
class="w-full sm:w-80 text-sm bg-white text-slate-400 inline-flex items-center justify-between leading-5 pl-3 pr-2 py-2 rounded border border-slate-200 hover:border-slate-300 shadow-sm whitespace-nowrap dark:text-slate-500 dark:bg-slate-800 dark:border-slate-700 dark:hover:border-slate-600"
class="w-full sm:w-80 text-sm bg-white text-zinc-400 inline-flex items-center justify-between leading-5 pl-3 pr-2 py-2 rounded border border-zinc-200 hover:border-zinc-300 shadow-sm whitespace-nowrap dark:text-zinc-500 dark:bg-zinc-800 dark:border-zinc-700 dark:hover:border-zinc-600"
@click.prevent="searchOpen = true"
@keydown.slash.window="searchOpen = true"
>
<div class="flex items-center">
<i class="fa-solid fa-magnifying-glass w-4 h-4 mr-3 text-slate-400"></i>
<i class="fa-solid fa-magnifying-glass w-4 h-4 mr-3 text-zinc-400"></i>
<span>Search<span class="hidden sm:inline"> docs</span>...</span>
</div>
<kbd class="hidden sm:inline-flex items-center justify-center h-5 w-5 text-xs font-medium text-slate-500 rounded border border-slate-200 dark:bg-slate-700 dark:text-slate-400 dark:border-slate-600">/</kbd>
<kbd class="hidden sm:inline-flex items-center justify-center h-5 w-5 text-xs font-medium text-zinc-500 rounded border border-zinc-200 dark:bg-zinc-700 dark:text-zinc-400 dark:border-zinc-600">/</kbd>
</button>
{{-- Search modal placeholder --}}
<template x-teleport="body">
<div x-show="searchOpen" x-cloak>
<div class="fixed inset-0 bg-slate-900/20 z-50" @click="searchOpen = false" @keydown.escape.window="searchOpen = false"></div>
<div class="fixed inset-0 bg-zinc-900/20 z-50" @click="searchOpen = false" @keydown.escape.window="searchOpen = false"></div>
<div class="fixed inset-0 z-50 overflow-hidden flex items-start top-20 justify-center px-4">
<div class="bg-white overflow-auto max-w-2xl w-full max-h-[80vh] rounded-lg shadow-lg dark:bg-slate-800 p-4" @click.outside="searchOpen = false">
<p class="text-slate-500 dark:text-slate-400 text-center py-8">Search coming soon...</p>
<div class="bg-white overflow-auto max-w-2xl w-full max-h-[80vh] rounded-lg shadow-lg dark:bg-zinc-800 p-4" @click.outside="searchOpen = false">
<p class="text-zinc-500 dark:text-zinc-400 text-center py-8">Search coming soon...</p>
</div>
</div>
</div>
@ -82,14 +74,14 @@
{{-- Desktop nav --}}
<nav class="flex items-center gap-6">
<a href="{{ route('api.guides') }}" class="text-sm {{ request()->routeIs('api.guides*') ? 'font-medium text-blue-600 dark:text-blue-400' : 'text-slate-600 hover:text-slate-900 dark:text-slate-400 dark:hover:text-slate-200' }}">Guides</a>
<a href="{{ route('api.reference') }}" class="text-sm {{ request()->routeIs('api.reference') ? 'font-medium text-blue-600 dark:text-blue-400' : 'text-slate-600 hover:text-slate-900 dark:text-slate-400 dark:hover:text-slate-200' }}">API Reference</a>
<a href="{{ route('api.guides') }}" class="text-sm {{ request()->routeIs('api.guides*') ? 'font-medium text-cyan-600 dark:text-cyan-400' : 'text-zinc-600 hover:text-zinc-900 dark:text-zinc-400 dark:hover:text-zinc-200' }}">Guides</a>
<a href="{{ route('api.reference') }}" class="text-sm {{ request()->routeIs('api.reference') ? 'font-medium text-cyan-600 dark:text-cyan-400' : 'text-zinc-600 hover:text-zinc-900 dark:text-zinc-400 dark:hover:text-zinc-200' }}">API Reference</a>
{{-- API Explorer dropdown --}}
<div class="relative" x-data="{ open: false }" @click.outside="open = false">
<button
@click="open = !open"
class="text-sm flex items-center gap-1 {{ request()->routeIs('api.swagger', 'api.scalar', 'api.redoc') ? 'font-medium text-blue-600 dark:text-blue-400' : 'text-slate-600 hover:text-slate-900 dark:text-slate-400 dark:hover:text-slate-200' }}"
class="text-sm flex items-center gap-1 {{ request()->routeIs('api.swagger', 'api.scalar', 'api.redoc') ? 'font-medium text-cyan-600 dark:text-cyan-400' : 'text-zinc-600 hover:text-zinc-900 dark:text-zinc-400 dark:hover:text-zinc-200' }}"
>
API Explorer
<svg class="w-4 h-4 transition-transform" :class="{ 'rotate-180': open }" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor">
@ -104,18 +96,18 @@
x-transition:leave="transition ease-in duration-75"
x-transition:leave-start="opacity-100 scale-100"
x-transition:leave-end="opacity-0 scale-95"
class="absolute right-0 mt-2 w-40 origin-top-right rounded-lg bg-white shadow-lg ring-1 ring-slate-200 dark:bg-slate-800 dark:ring-slate-700"
class="absolute right-0 mt-2 w-40 origin-top-right rounded-lg bg-white shadow-lg ring-1 ring-zinc-200 dark:bg-zinc-800 dark:ring-zinc-700"
x-cloak
>
<div class="py-1">
<a href="{{ route('api.swagger') }}" class="block px-4 py-2 text-sm text-slate-700 hover:bg-slate-100 dark:text-slate-300 dark:hover:bg-slate-700 {{ request()->routeIs('api.swagger') ? 'bg-slate-100 dark:bg-slate-700' : '' }}">
<i class="fa-solid fa-flask w-4 mr-2 text-slate-400"></i>Swagger
<a href="{{ route('api.swagger') }}" class="block px-4 py-2 text-sm text-zinc-700 hover:bg-zinc-100 dark:text-zinc-300 dark:hover:bg-zinc-700 {{ request()->routeIs('api.swagger') ? 'bg-zinc-100 dark:bg-zinc-700' : '' }}">
<i class="fa-solid fa-flask w-4 mr-2 text-zinc-400"></i>Swagger
</a>
<a href="{{ route('api.scalar') }}" class="block px-4 py-2 text-sm text-slate-700 hover:bg-slate-100 dark:text-slate-300 dark:hover:bg-slate-700 {{ request()->routeIs('api.scalar') ? 'bg-slate-100 dark:bg-slate-700' : '' }}">
<i class="fa-solid fa-bolt w-4 mr-2 text-slate-400"></i>Scalar
<a href="{{ route('api.scalar') }}" class="block px-4 py-2 text-sm text-zinc-700 hover:bg-zinc-100 dark:text-zinc-300 dark:hover:bg-zinc-700 {{ request()->routeIs('api.scalar') ? 'bg-zinc-100 dark:bg-zinc-700' : '' }}">
<i class="fa-solid fa-bolt w-4 mr-2 text-zinc-400"></i>Scalar
</a>
<a href="{{ route('api.redoc') }}" class="block px-4 py-2 text-sm text-slate-700 hover:bg-slate-100 dark:text-slate-300 dark:hover:bg-slate-700 {{ request()->routeIs('api.redoc') ? 'bg-slate-100 dark:bg-slate-700' : '' }}">
<i class="fa-solid fa-book w-4 mr-2 text-slate-400"></i>ReDoc
<a href="{{ route('api.redoc') }}" class="block px-4 py-2 text-sm text-zinc-700 hover:bg-zinc-100 dark:text-zinc-300 dark:hover:bg-zinc-700 {{ request()->routeIs('api.redoc') ? 'bg-zinc-100 dark:bg-zinc-700' : '' }}">
<i class="fa-solid fa-book w-4 mr-2 text-zinc-400"></i>ReDoc
</a>
</div>
</div>
@ -125,7 +117,7 @@
<button
x-data="{ dark: document.documentElement.classList.contains('dark') }"
type="button"
class="p-2 text-slate-500 hover:text-slate-700 dark:text-slate-400 dark:hover:text-slate-200"
class="p-2 text-zinc-500 hover:text-zinc-700 dark:text-zinc-400 dark:hover:text-zinc-200"
aria-label="Toggle dark mode"
x-on:click="dark = !dark; document.documentElement.classList.toggle('dark'); localStorage.setItem('flux.appearance', dark ? 'dark' : 'light')"
>
@ -144,16 +136,15 @@
</main>
{{-- Site footer --}}
<footer class="border-t border-slate-200 dark:border-slate-800">
<footer class="border-t border-zinc-200 dark:border-zinc-800">
<div class="max-w-7xl mx-auto px-4 sm:px-6 py-8">
<div class="flex flex-col md:flex-row md:items-center md:justify-between gap-4">
<div class="text-sm text-slate-500 dark:text-slate-400">
&copy; {{ date('Y') }} Host UK. All rights reserved.
<div class="text-sm text-zinc-500 dark:text-zinc-400">
&copy; {{ date('Y') }} {{ $appName }}. All rights reserved.
</div>
<div class="flex gap-6">
<a href="https://host.uk.com" class="text-sm text-slate-500 hover:text-slate-700 dark:text-slate-400 dark:hover:text-slate-200">Host UK</a>
<a href="{{ route('api.openapi.json') }}" class="text-sm text-slate-500 hover:text-slate-700 dark:text-slate-400 dark:hover:text-slate-200">OpenAPI Spec</a>
<a href="{{ str_replace('api.', 'mcp.', request()->getSchemeAndHttpHost()) }}" class="text-sm text-slate-500 hover:text-slate-700 dark:text-slate-400 dark:hover:text-slate-200">MCP Portal</a>
<a href="{{ route('api.openapi.json') }}" class="text-sm text-zinc-500 hover:text-zinc-700 dark:text-zinc-400 dark:hover:text-zinc-200">OpenAPI Spec</a>
<a href="{{ str_replace('api.', 'mcp.', request()->getSchemeAndHttpHost()) }}" class="text-sm text-zinc-500 hover:text-zinc-700 dark:text-zinc-400 dark:hover:text-zinc-200">MCP Portal</a>
</div>
</div>
</div>
@ -161,6 +152,7 @@
</div>
@fluxScripts
@stack('scripts')
</body>
</html>

View file

@ -1,8 +1,8 @@
@props(['method', 'path', 'description', 'body' => null, 'response'])
<div class="mb-8 border border-slate-200 dark:border-slate-700 rounded-sm overflow-hidden">
<div class="mb-8 border border-zinc-200 dark:border-zinc-700 rounded-sm overflow-hidden">
{{-- Header --}}
<div class="flex items-center gap-4 px-4 py-3 bg-slate-50 dark:bg-slate-800/50 border-b border-slate-200 dark:border-slate-700">
<div class="flex items-center gap-4 px-4 py-3 bg-zinc-50 dark:bg-zinc-800/50 border-b border-zinc-200 dark:border-zinc-700">
<span class="inline-flex items-center justify-center px-2 py-1 text-xs font-semibold rounded
@if($method === 'GET') bg-blue-100 text-blue-700 dark:bg-blue-900/30 dark:text-blue-400
@elseif($method === 'POST') bg-green-100 text-green-700 dark:bg-green-900/30 dark:text-green-400
@ -11,26 +11,26 @@
@endif">
{{ $method }}
</span>
<code class="text-sm font-pt-mono text-slate-800 dark:text-slate-200">{{ $path }}</code>
<code class="text-sm font-pt-mono text-zinc-800 dark:text-zinc-200">{{ $path }}</code>
</div>
{{-- Body --}}
<div class="p-4">
<p class="text-slate-600 dark:text-slate-400 mb-4">{{ $description }}</p>
<p class="text-zinc-600 dark:text-zinc-400 mb-4">{{ $description }}</p>
@if($body)
<div class="mb-4">
<h4 class="text-xs font-semibold text-slate-500 dark:text-slate-400 uppercase tracking-wider mb-2">Request Body</h4>
<div class="bg-slate-800 rounded-sm overflow-hidden">
<pre class="overflow-x-auto p-3 text-sm"><code class="font-pt-mono text-slate-300">{{ $body }}</code></pre>
<h4 class="text-xs font-semibold text-zinc-500 dark:text-zinc-400 uppercase tracking-wider mb-2">Request Body</h4>
<div class="bg-zinc-800 rounded-sm overflow-hidden">
<pre class="overflow-x-auto p-3 text-sm"><code class="font-pt-mono text-zinc-300">{{ $body }}</code></pre>
</div>
</div>
@endif
<div>
<h4 class="text-xs font-semibold text-slate-500 dark:text-slate-400 uppercase tracking-wider mb-2">Response</h4>
<div class="bg-slate-800 rounded-sm overflow-hidden">
<pre class="overflow-x-auto p-3 text-sm"><code class="font-pt-mono text-slate-300">{{ $response }}</code></pre>
<h4 class="text-xs font-semibold text-zinc-500 dark:text-zinc-400 uppercase tracking-wider mb-2">Response</h4>
<div class="bg-zinc-800 rounded-sm overflow-hidden">
<pre class="overflow-x-auto p-3 text-sm"><code class="font-pt-mono text-zinc-300">{{ $response }}</code></pre>
</div>
</div>
</div>

View file

@ -3,8 +3,8 @@
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>API Reference - Host UK</title>
<meta name="description" content="Host UK API Reference - ReDoc documentation">
<title>API Reference - {{ config('core.app.name', 'Core PHP') }}</title>
<meta name="description" content="{{ config('core.app.name', 'Core PHP') }} API Reference - ReDoc documentation">
<link rel="icon" type="image/png" href="/favicon.png">
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600&display=swap" rel="stylesheet">
<style>

View file

@ -6,39 +6,29 @@
<div class="flex">
{{-- Sidebar --}}
<aside class="hidden lg:block fixed left-0 top-16 md:top-20 bottom-0 w-64 border-r border-slate-200 dark:border-slate-800">
<aside class="hidden lg:block fixed left-0 top-16 md:top-20 bottom-0 w-64 border-r border-zinc-200 dark:border-zinc-800">
<div class="h-full px-4 py-8 overflow-y-auto no-scrollbar">
<nav>
<h3 class="text-xs font-semibold text-slate-400 uppercase tracking-wider mb-3">Resources</h3>
<h3 class="text-xs font-semibold text-zinc-400 uppercase tracking-wider mb-3">Resources</h3>
<ul class="space-y-1">
<li>
<a href="#workspaces" data-scrollspy-link class="block px-3 py-2 text-sm text-slate-600 hover:text-slate-900 dark:text-slate-400 dark:hover:text-slate-200 rounded-sm relative before:absolute before:inset-y-1 before:left-0 before:w-0.5 before:rounded-full">
Workspaces
<a href="#brain" data-scrollspy-link class="block px-3 py-2 text-sm text-zinc-600 hover:text-zinc-900 dark:text-zinc-400 dark:hover:text-zinc-200 rounded-sm relative before:absolute before:inset-y-1 before:left-0 before:w-0.5 before:rounded-full">
Brain Memory
</a>
</li>
<li>
<a href="#biolinks" data-scrollspy-link class="block px-3 py-2 text-sm text-slate-600 hover:text-slate-900 dark:text-slate-400 dark:hover:text-slate-200 rounded-sm relative before:absolute before:inset-y-1 before:left-0 before:w-0.5 before:rounded-full">
Biolinks
<a href="#score" data-scrollspy-link class="block px-3 py-2 text-sm text-zinc-600 hover:text-zinc-900 dark:text-zinc-400 dark:hover:text-zinc-200 rounded-sm relative before:absolute before:inset-y-1 before:left-0 before:w-0.5 before:rounded-full">
Content Scoring
</a>
</li>
<li>
<a href="#blocks" data-scrollspy-link class="block px-3 py-2 text-sm text-slate-600 hover:text-slate-900 dark:text-slate-400 dark:hover:text-slate-200 rounded-sm relative before:absolute before:inset-y-1 before:left-0 before:w-0.5 before:rounded-full">
Blocks
<a href="#collections" data-scrollspy-link class="block px-3 py-2 text-sm text-zinc-600 hover:text-zinc-900 dark:text-zinc-400 dark:hover:text-zinc-200 rounded-sm relative before:absolute before:inset-y-1 before:left-0 before:w-0.5 before:rounded-full">
Collections
</a>
</li>
<li>
<a href="#shortlinks" data-scrollspy-link class="block px-3 py-2 text-sm text-slate-600 hover:text-slate-900 dark:text-slate-400 dark:hover:text-slate-200 rounded-sm relative before:absolute before:inset-y-1 before:left-0 before:w-0.5 before:rounded-full">
Short Links
</a>
</li>
<li>
<a href="#qrcodes" data-scrollspy-link class="block px-3 py-2 text-sm text-slate-600 hover:text-slate-900 dark:text-slate-400 dark:hover:text-slate-200 rounded-sm relative before:absolute before:inset-y-1 before:left-0 before:w-0.5 before:rounded-full">
QR Codes
</a>
</li>
<li>
<a href="#analytics" data-scrollspy-link class="block px-3 py-2 text-sm text-slate-600 hover:text-slate-900 dark:text-slate-400 dark:hover:text-slate-200 rounded-sm relative before:absolute before:inset-y-1 before:left-0 before:w-0.5 before:rounded-full">
Analytics
<a href="#health" data-scrollspy-link class="block px-3 py-2 text-sm text-zinc-600 hover:text-zinc-900 dark:text-zinc-400 dark:hover:text-zinc-200 rounded-sm relative before:absolute before:inset-y-1 before:left-0 before:w-0.5 before:rounded-full">
Health
</a>
</li>
</ul>
@ -50,206 +40,104 @@
<div class="lg:pl-64 w-full">
<div class="max-w-4xl mx-auto px-4 sm:px-6 py-12">
<h1 class="h1 mb-4 text-slate-800 dark:text-slate-100">API Reference</h1>
<p class="text-xl text-slate-600 dark:text-slate-400 mb-4">
Complete reference for all Host UK API endpoints.
<h1 class="h1 mb-4 text-zinc-800 dark:text-zinc-100">API Reference</h1>
<p class="text-xl text-zinc-600 dark:text-zinc-400 mb-4">
Complete reference for all API endpoints.
</p>
<p class="text-slate-600 dark:text-slate-400 mb-12">
Base URL: <code class="px-2 py-1 bg-slate-100 dark:bg-slate-800 rounded text-sm font-pt-mono">https://api.host.uk.com/api/v1</code>
<p class="text-zinc-600 dark:text-zinc-400 mb-12">
Base URL: <code class="px-2 py-1 bg-zinc-100 dark:bg-zinc-800 rounded text-sm font-pt-mono">https://api.lthn.ai/v1</code>
</p>
{{-- Workspaces --}}
<section id="workspaces" data-scrollspy-target class="mb-16">
<h2 class="h2 mb-6 text-slate-800 dark:text-slate-100 pb-2 border-b border-slate-200 dark:border-slate-700">Workspaces</h2>
<p class="text-slate-600 dark:text-slate-400 mb-6">
Workspaces are containers for your biolinks, short links, and other resources.
{{-- Brain Memory --}}
<section id="brain" data-scrollspy-target class="mb-16">
<h2 class="h2 mb-6 text-zinc-800 dark:text-zinc-100 pb-2 border-b border-zinc-200 dark:border-zinc-700">Brain Memory</h2>
<p class="text-zinc-600 dark:text-zinc-400 mb-6">
Store and retrieve agent memories with vector search. Powered by Qdrant for semantic retrieval.
</p>
@include('api::partials.endpoint', [
'method' => 'GET',
'path' => '/workspaces',
'description' => 'List all workspaces you have access to.',
'response' => '{"data": [{"id": 1, "name": "My Workspace", "slug": "my-workspace"}]}'
])
@include('api::partials.endpoint', [
'method' => 'GET',
'path' => '/workspaces/current',
'description' => 'Get the current workspace (from API key context).',
'response' => '{"data": {"id": 1, "name": "My Workspace", "slug": "my-workspace"}}'
])
@include('api::partials.endpoint', [
'method' => 'GET',
'path' => '/workspaces/{id}',
'description' => 'Get a specific workspace by ID.',
'response' => '{"data": {"id": 1, "name": "My Workspace", "slug": "my-workspace"}}'
])
</section>
{{-- Biolinks --}}
<section id="biolinks" data-scrollspy-target class="mb-16">
<h2 class="h2 mb-6 text-slate-800 dark:text-slate-100 pb-2 border-b border-slate-200 dark:border-slate-700">Biolinks</h2>
<p class="text-slate-600 dark:text-slate-400 mb-6">
Biolinks are customisable landing pages with blocks of content.
</p>
@include('api::partials.endpoint', [
'method' => 'GET',
'path' => '/bio',
'description' => 'List all biolinks in the workspace.',
'response' => '{"data": [{"id": 1, "url": "mypage", "type": "biolink"}]}'
'method' => 'POST',
'path' => '/brain/remember',
'description' => 'Store a new memory in the vector database.',
'body' => '{"content": "Go uses structural typing", "type": "fact", "project": "go-agentic", "tags": ["go", "typing"]}',
'response' => '{"id": "mem-abc-123", "type": "fact", "project": "go-agentic", "created_at": "2026-03-03T12:00:00+00:00"}'
])
@include('api::partials.endpoint', [
'method' => 'POST',
'path' => '/bio',
'description' => 'Create a new biolink.',
'body' => '{"url": "mypage", "type": "biolink"}',
'response' => '{"data": {"id": 1, "url": "mypage", "type": "biolink"}}'
])
@include('api::partials.endpoint', [
'method' => 'GET',
'path' => '/bio/{id}',
'description' => 'Get a specific biolink by ID.',
'response' => '{"data": {"id": 1, "url": "mypage", "type": "biolink"}}'
])
@include('api::partials.endpoint', [
'method' => 'PUT',
'path' => '/bio/{id}',
'description' => 'Update a biolink.',
'body' => '{"url": "newpage"}',
'response' => '{"data": {"id": 1, "url": "newpage", "type": "biolink"}}'
'path' => '/brain/recall',
'description' => 'Search memories by semantic query. Returns ranked results with confidence scores.',
'body' => '{"query": "how does typing work in Go", "top_k": 5, "project": "go-agentic"}',
'response' => '{"memories": [{"id": "mem-abc-123", "type": "fact", "content": "Go uses structural typing", "confidence": 0.95}], "scores": {"mem-abc-123": 0.87}}'
])
@include('api::partials.endpoint', [
'method' => 'DELETE',
'path' => '/bio/{id}',
'description' => 'Delete a biolink.',
'response' => '{"message": "Deleted successfully"}'
'path' => '/brain/forget/{id}',
'description' => 'Delete a specific memory by ID.',
'response' => '{"deleted": true}'
])
</section>
{{-- Blocks --}}
<section id="blocks" data-scrollspy-target class="mb-16">
<h2 class="h2 mb-6 text-slate-800 dark:text-slate-100 pb-2 border-b border-slate-200 dark:border-slate-700">Blocks</h2>
<p class="text-slate-600 dark:text-slate-400 mb-6">
Blocks are content elements within a biolink page.
{{-- Content Scoring --}}
<section id="score" data-scrollspy-target class="mb-16">
<h2 class="h2 mb-6 text-zinc-800 dark:text-zinc-100 pb-2 border-b border-zinc-200 dark:border-zinc-700">Content Scoring</h2>
<p class="text-zinc-600 dark:text-zinc-400 mb-6">
Score text for AI patterns and analyse linguistic imprints via the EaaS scoring engine.
</p>
@include('api::partials.endpoint', [
'method' => 'GET',
'path' => '/bio/{bioId}/blocks',
'description' => 'List all blocks for a biolink.',
'response' => '{"data": [{"id": 1, "type": "link", "data": {"title": "My Link"}}]}'
'method' => 'POST',
'path' => '/score/content',
'description' => 'Score text for AI-generated content patterns. Returns a score (0-1), confidence, and label.',
'body' => '{"text": "The text to analyse for AI patterns", "prompt": "Optional scoring prompt"}',
'response' => '{"score": 0.23, "confidence": 0.91, "label": "human"}'
])
@include('api::partials.endpoint', [
'method' => 'POST',
'path' => '/bio/{bioId}/blocks',
'description' => 'Add a new block to a biolink.',
'body' => '{"type": "link", "data": {"title": "My Link", "url": "https://example.com"}}',
'response' => '{"data": {"id": 1, "type": "link", "data": {"title": "My Link"}}}'
])
@include('api::partials.endpoint', [
'method' => 'PUT',
'path' => '/bio/{bioId}/blocks/{id}',
'description' => 'Update a block.',
'body' => '{"data": {"title": "Updated Link"}}',
'response' => '{"data": {"id": 1, "type": "link", "data": {"title": "Updated Link"}}}'
])
@include('api::partials.endpoint', [
'method' => 'DELETE',
'path' => '/bio/{bioId}/blocks/{id}',
'description' => 'Delete a block.',
'response' => '{"message": "Deleted successfully"}'
'path' => '/score/imprint',
'description' => 'Perform linguistic imprint analysis on text. Returns a unique imprint fingerprint.',
'body' => '{"text": "The text to analyse for linguistic patterns"}',
'response' => '{"imprint": "abc123def456", "confidence": 0.88}'
])
</section>
{{-- Short Links --}}
<section id="shortlinks" data-scrollspy-target class="mb-16">
<h2 class="h2 mb-6 text-slate-800 dark:text-slate-100 pb-2 border-b border-slate-200 dark:border-slate-700">Short Links</h2>
<p class="text-slate-600 dark:text-slate-400 mb-6">
Short links redirect to any URL with tracking.
{{-- Collections --}}
<section id="collections" data-scrollspy-target class="mb-16">
<h2 class="h2 mb-6 text-zinc-800 dark:text-zinc-100 pb-2 border-b border-zinc-200 dark:border-zinc-700">Collections</h2>
<p class="text-zinc-600 dark:text-zinc-400 mb-6">
Manage vector database collections for brain memory storage.
</p>
@include('api::partials.endpoint', [
'method' => 'GET',
'path' => '/shortlinks',
'description' => 'List all short links in the workspace.',
'response' => '{"data": [{"id": 1, "url": "abc123", "destination": "https://example.com"}]}'
])
@include('api::partials.endpoint', [
'method' => 'POST',
'path' => '/shortlinks',
'description' => 'Create a new short link.',
'body' => '{"destination": "https://example.com"}',
'response' => '{"data": {"id": 1, "url": "abc123", "destination": "https://example.com"}}'
'path' => '/brain/collections',
'description' => 'Ensure the workspace collection exists in the vector database. Creates it if missing.',
'response' => '{"status": "ok"}'
])
</section>
{{-- QR Codes --}}
<section id="qrcodes" data-scrollspy-target class="mb-16">
<h2 class="h2 mb-6 text-slate-800 dark:text-slate-100 pb-2 border-b border-slate-200 dark:border-slate-700">QR Codes</h2>
<p class="text-slate-600 dark:text-slate-400 mb-6">
Generate customisable QR codes for biolinks or any URL.
{{-- Health --}}
<section id="health" data-scrollspy-target class="mb-16">
<h2 class="h2 mb-6 text-zinc-800 dark:text-zinc-100 pb-2 border-b border-zinc-200 dark:border-zinc-700">Health</h2>
<p class="text-zinc-600 dark:text-zinc-400 mb-6">
Health check endpoints. These do not require authentication.
</p>
@include('api::partials.endpoint', [
'method' => 'GET',
'path' => '/bio/{id}/qr',
'description' => 'Get QR code data for a biolink.',
'response' => '{"data": {"svg": "<svg>...</svg>", "url": "https://lt.hn/mypage"}}'
])
@include('api::partials.endpoint', [
'method' => 'GET',
'path' => '/bio/{id}/qr/download',
'description' => 'Download QR code as PNG/SVG. Query params: format (png|svg), size (100-2000).',
'response' => 'Binary image data'
])
@include('api::partials.endpoint', [
'method' => 'POST',
'path' => '/qr/generate',
'description' => 'Generate QR code for any URL.',
'body' => '{"url": "https://example.com", "format": "svg", "size": 300}',
'response' => '{"data": {"svg": "<svg>...</svg>"}}'
])
@include('api::partials.endpoint', [
'method' => 'GET',
'path' => '/qr/options',
'description' => 'Get available QR code customisation options.',
'response' => '{"data": {"formats": ["png", "svg"], "sizes": {"min": 100, "max": 2000}}}'
])
</section>
{{-- Analytics --}}
<section id="analytics" data-scrollspy-target class="mb-16">
<h2 class="h2 mb-6 text-slate-800 dark:text-slate-100 pb-2 border-b border-slate-200 dark:border-slate-700">Analytics</h2>
<p class="text-slate-600 dark:text-slate-400 mb-6">
View analytics data for your biolinks.
</p>
@include('api::partials.endpoint', [
'method' => 'GET',
'path' => '/bio/{id}/analytics',
'description' => 'Get analytics for a biolink. Query params: period (7d|30d|90d).',
'response' => '{"data": {"views": 1234, "clicks": 567, "unique_visitors": 890}}'
'path' => '/score/health',
'description' => 'Check the health of the scoring engine and its upstream services.',
'response' => '{"status": "healthy", "upstream_status": 200}'
])
</section>
{{-- CTA --}}
<div class="mt-12 p-6 bg-slate-50 dark:bg-slate-800 border border-slate-200 dark:border-slate-700 rounded-sm text-center">
<h3 class="h4 mb-2 text-slate-800 dark:text-slate-100">Try it out</h3>
<p class="text-slate-600 dark:text-slate-400 mb-4">Test endpoints interactively with Swagger UI.</p>
<a href="{{ route('api.swagger') }}" class="btn text-white bg-blue-600 hover:bg-blue-700 px-6 py-2 rounded-sm font-medium">
<div class="mt-12 p-6 bg-zinc-50 dark:bg-zinc-800 border border-zinc-200 dark:border-zinc-700 rounded-sm text-center">
<h3 class="h4 mb-2 text-zinc-800 dark:text-zinc-100">Try it out</h3>
<p class="text-zinc-600 dark:text-zinc-400 mb-4">Test endpoints interactively with Swagger UI.</p>
<a href="{{ route('api.swagger') }}" class="btn text-white bg-cyan-600 hover:bg-cyan-700 px-6 py-2 rounded-sm font-medium">
Open Swagger UI
</a>
</div>

View file

@ -3,8 +3,8 @@
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>API Reference - Host UK</title>
<meta name="description" content="Host UK API Reference - Interactive documentation with code samples">
<title>API Reference - {{ config('core.app.name', 'Core PHP') }}</title>
<meta name="description" content="{{ config('core.app.name', 'Core PHP') }} API Reference - Interactive documentation with code samples">
<link rel="icon" type="image/png" href="/favicon.png">
<style>
* { box-sizing: border-box; }
@ -60,8 +60,8 @@
"clientKey": "guzzle"
},
"metaData": {
"title": "Host UK API",
"description": "API documentation for Host UK services"
"title": "{{ config('core.app.name', 'Core PHP') }} API",
"description": "API documentation for {{ config('core.app.name', 'Core PHP') }}"
}
}'
></script>

View file

@ -20,13 +20,13 @@
@section('content')
<div class="max-w-7xl mx-auto px-4 sm:px-6 py-8">
<div class="mb-8">
<h1 class="h2 mb-2 text-slate-800 dark:text-slate-100">Swagger UI</h1>
<p class="text-slate-600 dark:text-slate-400">
<h1 class="h2 mb-2 text-zinc-800 dark:text-zinc-100">Swagger UI</h1>
<p class="text-zinc-600 dark:text-zinc-400">
Interactive API explorer. Try out endpoints directly from your browser.
</p>
</div>
<div id="swagger-ui" class="bg-white dark:bg-slate-800 rounded-sm border border-slate-200 dark:border-slate-700 p-4"></div>
<div id="swagger-ui" class="bg-white dark:bg-zinc-800 rounded-sm border border-zinc-200 dark:border-zinc-700 p-4"></div>
</div>
@endsection