Application pulse
Measure what matters, without the noise.
Heartbeat lets you record lightweight events from your application and view their rhythm over time. Track tiny interactions or critical milestones with the same endpoint.
Free-text events
Name events any way you like, from auth flows to tiny function calls.
Flexible timing
Send heartbeats with a custom timestamp when long tasks finish.
Integration Guide
Complete API reference and code examples for integrating Hartslag into your applications.
Hartslag Integration Guide (Heartbeat API)
Note: This guide focuses on safe, low-impact event tracking. Heartbeats should never block user requests or break your app.
Overview
Hartslag is a lightweight event tracking service. You send timestamped heartbeats whenever something meaningful happens (user activity, background jobs, business events), and Hartslag aggregates counts over time.
What is a “heartbeat”?
A heartbeat is a single, timestamped occurrence of an event:
- Real-time activity monitoring: Track actions and system events as they happen
- Aggregate reporting: View event counts aggregated by time periods (today, week, month, total)
- Flexible event tracking: Use any event name (recommended: kebab-case)
- Weighted metrics: Use
weightto track values like durations, file sizes, or amounts
How It Works
- Your app sends a
POSTrequest to the API with aneventname (and optional metadata) - Hartslag records the heartbeat
- You view aggregated metrics in the dashboard
- You can export historical data as CSV
Base URL: http://hartslag.no
The base URL is the URL of your Hartslag instance. When viewing the hosted docs, the site will typically show the correct base URL for that environment.
Quick Start
Important: Send heartbeats asynchronously (queue/background) whenever possible. Tracking must never slow down user requests.
1. Get Your API Token
- Log in to Hartslag
- Create or select an Application
- Copy the API token (bearer token)
2. Send Your First Heartbeat
BASE_URL="http://hartslag.no"
TOKEN="YOUR_TOKEN_HERE"
curl -X POST "$BASE_URL/api/v1/heartbeat" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"event": "user-login",
"weight": 1,
"user_id": 123,
"created_at": "2024-01-17T10:30:00Z"
}'
Success: 201 Created
Response body (current): empty JSON (e.g. []).
API Reference
POST /api/v1/heartbeat
Records a single heartbeat.
Headers
Authorization: Bearer {token}(required)Content-Type: application/json(required)
Body parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
event |
string | Yes | Event name. Recommended: kebab-case (e.g. user-login). |
weight |
integer | No | Default: 1. Use for weighted metrics (durations, amounts, etc.). |
user_id |
integer | No | Optional user identifier for per-user activity. |
created_at |
string/date | No | Optional timestamp. The API accepts Laravel “date” formats. Recommended: ISO 8601 / RFC 3339 (e.g. 2024-01-17T10:30:00Z). |
Success response
- Status:
201 Created - Body: empty JSON (current)
Error responses
-
401 Unauthorized: Missing/invalid token{"error": "Unauthorized"} -
422 Unprocessable Entity: Validation error{ "message": "The event field is required.", "errors": { "event": ["The event field is required."] } }
Idempotency / retries
Requests are not idempotent. If your client retries the same request, it may be counted multiple times.
If this matters for a specific event, de-duplicate on the client side (or accept small overcounting for non-critical metrics).
Code Examples
PHP (Laravel) — recommended pattern
Store base URL + token in config, and send via queue/job.
use Illuminate\Support\Facades\Http;
class HartslagClient
{
public function send(string $event, int $weight = 1, ?int $userId = null, $createdAt = null): void
{
$baseUrl = rtrim(config('services.hartslag.url'), '/');
$token = config('services.hartslag.token');
// Never let tracking break the main flow.
try {
Http::withToken($token)
->acceptJson()
->asJson()
->connectTimeout(2)
->timeout(5)
->retry(2, 150, throw: false)
->post("{$baseUrl}/api/v1/heartbeat", [
'event' => $event,
'weight' => $weight,
'user_id' => $userId,
'created_at' => $createdAt ?? now()->toISOString(),
]);
} catch (\Throwable $e) {
\Log::warning('Hartslag heartbeat failed', ['error' => $e->getMessage()]);
}
}
}
Config (config/services.php):
'hartslag' => [
'url' => env('HARTSLAG_URL', 'http://hartslag.no'),
'token' => env('HARTSLAG_TOKEN'),
],
Queue it (example):
Laravel best practice is to use a dedicated Job that implements ShouldQueue.
php artisan make:job SendHartslagHeartbeat
SendHartslagHeartbeat::dispatch('user-registered', auth()->id())
->onQueue('low');
JavaScript (Node.js)
const axios = require('axios');
async function sendHeartbeat(baseUrl, token, payload) {
try {
const res = await axios.post(
`${baseUrl.replace(/\/$/, '')}/api/v1/heartbeat`,
payload,
{
timeout: 2000,
headers: {
Authorization: `Bearer ${token}`,
'Content-Type': 'application/json',
},
}
);
return res.status === 201;
} catch (err) {
// Log and continue; tracking must be non-critical.
return false;
}
}
Browser/Frontend
Do not call the Hartslag API directly from the browser (you would expose your token).
Recommended pattern:
- Frontend → your backend (no Hartslag token)
- Backend → Hartslag API (token stored server-side)
Best Practices
Event naming
Recommended: descriptive, kebab-case event names.
Good:
user-loginpayment-completedemail-sent
Avoid:
event1,test,fooUserLogin
Weight usage
Use weight for meaningful metrics:
// Response time in ms
$ms = 150;
$client->send('api-call', weight: $ms);
// Payment amount in cents
$client->send('payment-completed', weight: 1999, userId: $user->id);
Time handling (created_at)
- Omit
created_atfor “now” (recommended) - Use ISO 8601 / RFC 3339 when backdating
- Avoid future timestamps unless you intentionally want that behavior
Security
- Keep tokens server-side (env vars / secrets manager)
- Use HTTPS
- Rotate tokens when needed
Troubleshooting
401 Unauthorized
- Check bearer token
- Check
Authorization: Bearer ...format
422 Validation Error
- Ensure
eventis present and non-empty weightanduser_idmust be integers if provided- Use a valid date for
created_at(ISO 8601 recommended)
Timeouts / flaky networks
- Use short timeouts (2–5s)
- Retry a small number of times
- Prefer queue/background for non-critical tracking
Version
API Version: v1
Need help? Contact your system administrator or log in to your dashboard