PulseWise API Documentation
Welcome to the PulseWise API documentation. This RESTful API provides access to healthcare data and analytics.
Base URL
https://pulsewise.com
Version
v1
Response Format
JSON
Getting Started
Quick Start Guide
- Register for an API account and obtain your API key
- Include your API key in the Authorization header of all requests
- Make your first API call to test connectivity
- Explore the available endpoints below
Test Your Connection
curl -H "Authorization: Bearer YOUR_API_KEY" \
https://pulsewise.com/api/v1/status
Authentication
PulseWise API uses Bearer token authentication. Include your API key in the Authorization header:
Authorization: Bearer YOUR_API_KEY
API Endpoints
entitlement it returns
403 FEATURE_RESTRICTED.
Parameters
| Name | Type | Required | Description |
|---|---|---|---|
Code Examples
Try It Live
Response:
Error:
Sample Response
Log Monitor ingest
Your application posts an hourly summary of what happened; PulseWise tells you when those numbers stop looking like your application. Everything below is what you need to write a client — see Log Monitor to create a source and get its key.
Send every hour, including empty ones
Post each hour even when every count is zero. A zero is a measurement; a missing hour is the absence of one. If quiet hours are skipped, "nobody used the site" becomes indistinguishable from "the site stopped reporting" — and a source that stops reporting is treated as down. Skipping empty hours will get you paged for an outage you do not have.
https://pulsewise.com/api/v1/logs/ingest
X-Ingest-Key: ls_…. This is the source's own key, not a
pk_ API key — it can only write that one source's counts, and revoking it
does not affect your API access.
Request
{
"hours": [
{
"hour": "2026-09-08T14:00:00Z", // start of the hour, ISO 8601, UTC
"events": { // omit a type, or send 0 — same thing
"login_success": 42,
"login_failure": 3,
"password_reset": 1
},
"distinct_users": 31, // optional
"distinct_ips": 28, // optional
"new_ips": 4 // optional
}
]
}
Batching
Up to 48 hours per request. Send one hour at a time normally; send several to catch up after downtime.
Safe to retry
Re-posting an hour replaces it rather than adding to it, so a timeout you are unsure about can simply be retried. A duplicate would otherwise look exactly like a spike.
Accepted window
Backfill up to 7 days. Hours more than 90 minutes ahead of our clock are refused — check the clock on the sending server.
Event types
Send whichever apply. Unknown names are ignored rather than rejected, so a client reporting something newer than this server does not lose the hours that came with it.
| Key | Meaning | Assessed |
|---|---|---|
| login_success | Successful logins | Per hour, against that hour of the week |
| login_failure | Failed logins | Per hour, against that hour of the week |
| page_view | Page views | Per hour, against that hour of the week |
| password_reset | Password resets | Over a rolling window |
| user_created | Users created | Over a rolling window |
| user_deleted | Users deleted | Over a rolling window |
| role_changed | Roles changed | Over a rolling window |
| plugin_activated | Plugins activated | Over a rolling window |
| plugin_deactivated | Plugins deactivated | Over a rolling window |
| theme_changed | Themes changed | Over a rolling window |
| core_updated | Core updated | Over a rolling window |
| post_published | Posts published | Per hour, against that hour of the week |
| file_changed | Files changed | Over a rolling window |
| admin_login | Administrator logins | Over a rolling window |
Response
HTTP 202
{
"success": true,
"accepted": 1,
"rejected": [], // anything refused is named, never silently dropped
"next_expected_hour": "2026-09-08T15:00:00+00:00"
}
202 with a non-empty rejected array means some hours were not
stored and why. Log it — a client that ignores it will keep resending the same rejected hours
forever while its operator believes the site is monitored.
401 is an unknown or rotated key, 403 a paused source,
422 a malformed body.
A worked example
curl -X POST https://pulsewise.com/api/v1/logs/ingest -H "X-Ingest-Key: ls_your_key_here" -H "Content-Type: application/json" -d '{"hours":[{"hour":"2026-09-08T14:00:00Z","events":{"login_success":42,"login_failure":3},"distinct_users":31}]}'
In a Laravel application, an hourly scheduled command is usually the whole client:
// Runs hourly. Reports the hour that has just closed — including
// the hours when nothing happened, which is the point.
$hour = now()->utc()->subHour()->startOfHour();
Http::withHeaders(['X-Ingest-Key' => config('services.pulsewise.ingest_key')])
->post('https://pulsewise.com/api/v1/logs/ingest', [
'hours' => [[
'hour' => $hour->toIso8601String(),
'events' => [
'login_success' => Activity::type('login')->inHour($hour)->count(),
'login_failure' => Activity::type('login_failed')->inHour($hour)->count(),
'password_reset' => Activity::type('password_reset')->inHour($hour)->count(),
],
'distinct_users' => Activity::inHour($hour)->distinct('user_id')->count(),
]],
])->throw();
Send counts, not records
The endpoint accepts numbers and nothing else. No usernames, email addresses, IP addresses or log lines are wanted or stored — which keeps your users' data on your server, keeps this well clear of GDPR obligations, and means a breach here would reveal how busy you were and nothing more.
Site Monitor — scheduled malware scanning
The /api/malware/{target} endpoint above is a one-shot
check: it fetches a page and tells you whether known malware is being served right now. With
no history to compare against, it cannot tell you that something changed.
Site Monitor is the stateful half. Register a website and PulseWise fetches its home page — and any same-domain sub-pages you name — on a cadence you choose, then reports what it finds.
ALERT
Known malware is being served. A payload signature matched, packed JavaScript was found inlined in the page, or a script appeared from a host that is on a threat feed or that the site has never used before.
REVIEW
Something changed since the baseline you accepted. On a live site this is usually a plugin or theme update. Kept separate from alerts on purpose — blurring the two is what makes a scanner unreadable.
The baseline only moves when you move it
Changes are reported every scan until you accept the current state of the site. Nothing is adopted automatically, because a baseline that updates itself would absorb a slow injection one scan at a time.
Email only when there is something new
A report is sent only when a scan finds something the previous scan did not, so a change you have seen and chosen to leave unaccepted does not mail you every hour. A scan that finds nothing is silent.
Error Codes
| Code | Status | Description |
|---|---|---|
| 200 | OK | Request successful |
| 400 | Bad Request | Invalid request parameters |
| 401 | Unauthorized | Invalid or missing API key |
| 403 | Forbidden | Access denied |
| 404 | Not Found | Resource not found |
| 429 | Too Many Requests | Rate limit exceeded |
| 500 | Internal Server Error | Server error |
Rate Limits
The PulseWise API implements rate limiting to ensure fair usage and maintain service quality.
Rate Limit
1000 requests per hour
Rate Limit Headers
X-RateLimit-Limit
X-RateLimit-Remaining
X-RateLimit-Reset
Support
Need help with the PulseWise API? Here are some resources: