Skip to content

API Reference

Base URL: https://your-server/v1

All requests and responses use JSON. All authenticated endpoints require a Bearer token in the Authorization header. Every response includes an X-Request-Id header for tracing.

Authentication: obtain a token via POST /v1/auth/login. Access tokens expire after 1 hour (configurable). Use the refresh token to obtain a new access token without re-authenticating.


{
"error": "string",
"message": "human-readable detail"
}

Query params: ?page=1&per_page=20

{
"data": [...],
"page": 1,
"per_page": 20,
"total": 143
}

Auth required: No

Returns service health. Use this for monitoring and uptime checks.

Response 200:

{
"status": "ok",
"version": "1.2.3",
"db": "ok",
"cache": "ok"
}

Auth required: No

Create a new account.

Request:

{
"email": "user@example.com",
"password": "strong-password",
"display_name": "Alex"
}

Response 201:

{
"account_id": "01jwxyz...",
"email": "user@example.com",
"display_name": "Alex"
}

Auth required: No

Request:

{
"email": "user@example.com",
"password": "strong-password"
}

Response 200:

{
"access_token": "eyJ...",
"refresh_token": "eyJ...",
"expires_in": 3600
}

Auth required: No (uses refresh token in body)

Request:

{
"refresh_token": "eyJ..."
}

Response 200: Same shape as /login.


Auth required: Yes

Revokes the current refresh token.

Response 204: No body.


Auth required: No

Sends a password reset email if the address is registered. Always returns 200 (no account enumeration).

Request:

{ "email": "user@example.com" }

Response 200: { "message": "If that address is registered, a reset email has been sent." }


Auth required: No

Request:

{
"token": "reset-token-from-email",
"new_password": "new-strong-password"
}

Response 200: { "message": "Password updated." }


Auth required: Yes

Returns the authenticated user’s account.

Response 200:

{
"id": "01jwxyz...",
"email": "user@example.com",
"display_name": "Alex",
"created_at": "2026-03-15T10:00:00Z"
}

Auth required: Yes

Update display name or email.

Request (all fields optional):

{
"display_name": "New Name",
"email": "new@example.com"
}

Response 200: Updated account object.


Auth required: Yes

Returns another account’s public profile. Used by partners to look up linked accounts.

Response 200: Account object (subset — no email for non-self).


Auth required: Yes

Register a new device. Called by the agent during enrollment.

Request:

{
"enrollment_token": "tok_...",
"platform": "windows",
"hostname": "alex-laptop",
"agent_version": "1.2.3",
"client_cert_csr": "-----BEGIN CERTIFICATE REQUEST-----..."
}

Response 201:

{
"device_id": "01jwxyz...",
"client_cert": "-----BEGIN CERTIFICATE-----...",
"blocklist_version": 42,
"config": { ... }
}

Auth required: Yes

List all devices belonging to the authenticated account.

Response 200:

{
"data": [
{
"id": "01jwxyz...",
"hostname": "alex-laptop",
"platform": "windows",
"status": "active",
"last_heartbeat": "2026-03-15T10:55:00Z",
"enrollment_id": "01jwxyz...",
"agent_version": "1.2.3"
}
]
}

Auth required: Yes

Response 200: Single device object with full detail.


Auth required: Yes

Remove a device record. Does not unenroll — use POST /v1/enrollments/{id}/unenroll to initiate the unenrollment flow.

Response 204: No body.


Auth required: Yes

Called by the agent on a regular interval (default: every 5 minutes). Records the device as alive and returns any pending configuration changes.

Request:

{
"blocklist_version": 42,
"agent_version": "1.2.3",
"status": "healthy",
"integrity_ok": true
}

Response 200:

{
"blocklist_version": 43,
"config_changed": false,
"commands": []
}

commands may contain server-initiated actions (e.g., force_blocklist_update).


Auth required: Yes

Returns the full configuration for the device including blocklist server URL, heartbeat interval, and protection settings.

Response 200:

{
"blocklist_url": "https://your-server/v1/blocklist/delta",
"heartbeat_interval_secs": 300,
"protection": {
"dns_blocking": true,
"app_blocking": false,
"bypass_detection": true
},
"reporting": {
"send_block_events": true,
"send_tamper_events": true
}
}

Auth required: Yes

Create a new enrollment (generates an enrollment token for use during agent setup).

Request:

{
"device_id": "01jwxyz...",
"tier": "partner",
"unenroll_delay_hours": 48,
"partner_account_id": "01jwxyz..."
}

tier values: self, partner, authority.

Response 201:

{
"id": "01jwxyz...",
"token": "tok_...",
"qr_url": "https://your-server/v1/enrollments/01jwxyz.../qr",
"tier": "partner",
"status": "pending"
}

Auth required: Yes

List enrollments for the authenticated account (as enrollee or as partner/authority).

Response 200: Paginated list of enrollment objects.


Auth required: Yes

Response 200: Full enrollment object including protection config and unenrollment policy.


Auth required: Yes (enrollment authority only)

Update enrollment configuration (protection settings, reporting settings).

Request (all fields optional):

{
"unenroll_delay_hours": 72,
"protection": {
"bypass_detection": true
}
}

Response 200: Updated enrollment object.


Auth required: Yes

Initiate an unenrollment request. For self tier, starts the time-delay timer. For partner/authority tier, creates a pending request requiring approval.

Response 202:

{
"status": "pending_approval",
"eligible_at": null,
"message": "Unenrollment request sent to your accountability partner."
}

For self tier:

{
"status": "pending_delay",
"eligible_at": "2026-03-17T10:00:00Z"
}

POST /v1/enrollments/{id}/approve-unenroll

Section titled “POST /v1/enrollments/{id}/approve-unenroll”

Auth required: Yes (partner or authority role on this enrollment)

Approve a pending unenrollment request. Immediately completes unenrollment.

Response 200: { "status": "unenrolled" }


Auth required: Yes

Send an accountability partner invitation by email.

Request:

{ "email": "partner@example.com" }

Response 202: { "message": "Invitation sent." }


Auth required: Yes

List accepted and pending partner relationships.

Response 200:

{
"data": [
{
"id": "01jwxyz...",
"partner_account_id": "01jwxyz...",
"partner_display_name": "Sam",
"status": "accepted",
"created_at": "2026-03-01T00:00:00Z"
}
]
}

Auth required: Yes

Accept an incoming partner invitation.

Response 200: Partner relationship object.


Auth required: Yes

Remove a partner relationship. Does not unenroll devices.

Response 204: No body.


Organizations group devices and partners (used by therapy practices, court programs, families).

Auth required: Yes

Request:

{
"name": "Recovery Support Group",
"description": "optional"
}

Response 201: Organization object with id.


Auth required: Yes

List organizations the authenticated account belongs to.


Auth required: Yes (member)


Auth required: Yes (org admin)

Update name or description.


Auth required: Yes (org owner)


Auth required: Yes (org admin)

Invite a member by email.

Request:

{ "email": "member@example.com", "role": "member" }

role values: owner, admin, member.


Auth required: Yes (org member)


PATCH /v1/organizations/{id}/members/{member_id}

Section titled “PATCH /v1/organizations/{id}/members/{member_id}”

Auth required: Yes (org admin)

Update a member’s role.


DELETE /v1/organizations/{id}/members/{member_id}

Section titled “DELETE /v1/organizations/{id}/members/{member_id}”

Auth required: Yes (org admin)

Remove a member.


Auth required: Yes (org admin)

Assign a device to the organization.

Request: { "device_id": "01jwxyz..." }


Auth required: Yes (org member)


DELETE /v1/organizations/{id}/devices/{device_id}

Section titled “DELETE /v1/organizations/{id}/devices/{device_id}”

Auth required: Yes (org admin)


Auth required: Yes (org admin)

Create an enrollment token tied to the organization (used for bulk device enrollment).

Request:

{
"label": "Staff laptop enrollment",
"tier": "authority",
"max_uses": 100
}

Response 201: { "id": "...", "token": "tok_...", "qr_url": "..." }


Auth required: Yes (org admin)


DELETE /v1/organizations/{id}/tokens/{token_id}

Section titled “DELETE /v1/organizations/{id}/tokens/{token_id}”

Auth required: Yes (org admin)

Revoke a token immediately. Devices already enrolled remain enrolled.


GET /v1/organizations/{id}/tokens/{token_id}/qr

Section titled “GET /v1/organizations/{id}/tokens/{token_id}/qr”

Auth required: Yes (org admin)

Returns a QR code PNG for the enrollment token. Useful for printing or displaying on screen.


Auth required: Yes

Redeem an organization enrollment token. Called by the agent during device setup as an alternative to the standard enrollment flow.

Request: Same as POST /v1/devices (device registration payload).

Response 201: Device and enrollment objects.


Auth required: No

Returns the current blocklist version.

Response 200:

{
"version": 42,
"entry_count": 14892,
"updated_at": "2026-03-15T06:00:00Z",
"signature": "base64-encoded-ed25519-signature"
}

Auth required: No

Query params:

  • from_version (required): integer, the client’s current blocklist version

Returns only the changes since from_version. Agents call this on heartbeat when the version has changed.

Response 200:

{
"from_version": 41,
"to_version": 42,
"added": ["newgamblingsite.com", "..."],
"removed": ["cleanedup.com"],
"signature": "base64-encoded-ed25519-signature"
}

The signature covers the full serialised delta payload. Agents must verify this signature against the configured blocklist signing public key before applying updates.


Auth required: Yes

Submit a domain for blocklist review. Used by agents when heuristic matching flags an unknown domain.

Request:

{
"domain": "suspectdomain.com",
"reason": "heuristic_match",
"context": "optional additional detail"
}

Response 202: { "message": "Report queued for review." }


All analytics endpoints respect enrollment-tier visibility rules. Partners see aggregated data unless the account has granted detailed access.

Auth required: Yes

Query params: device_id, enrollment_id, from (ISO 8601), to (ISO 8601), interval (1h, 1d, 1w)

Response 200:

{
"series": [
{ "timestamp": "2026-03-15T00:00:00Z", "block_count": 12, "tamper_alerts": 0 }
]
}

Auth required: Yes

Returns week-over-week and month-over-month change percentages.

Response 200:

{
"block_count_wow": -15.2,
"block_count_mom": -42.0,
"active_devices": 3
}

Auth required: Yes

Returns totals for the current account.

Response 200:

{
"total_blocks_all_time": 8420,
"active_devices": 2,
"enrollments_active": 2,
"last_block_at": "2026-03-14T22:10:00Z"
}

Auth required: Yes

Returns block counts by hour-of-day and day-of-week for the requested period.

Query params: from, to, device_id (optional)

Response 200:

{
"heatmap": [
{ "day": 1, "hour": 22, "count": 7 }
]
}

Auth required: Yes

Download event data as CSV. Query params same as timeseries.

Response 200: Content-Type: text/csv


Auth required: Yes

Download a summary report as PDF.

Response 200: Content-Type: application/pdf


Auth required: Yes (org member)

Aggregate summary across all devices in the organisation.


Auth required: Yes

Batch ingest events from the agent. Agents buffer events locally and submit in batches.

Request:

{
"device_id": "01jwxyz...",
"events": [
{
"event_type": "block",
"domain": "gamblingsite.com",
"timestamp": "2026-03-15T10:00:00Z",
"layer": "dns"
},
{
"event_type": "tamper_attempt",
"detail": "dns_config_change",
"timestamp": "2026-03-15T10:05:00Z"
}
]
}

event_type values: block, bypass_attempt, tamper_attempt, enrollment_change, heartbeat_missed

Response 202: { "accepted": 2 }


Auth required: Yes

Query stored events.

Query params: device_id, enrollment_id, event_type, from, to, page, per_page

Response 200: Paginated list of event objects.


Auth required: Yes

Counts by event type for the current account.

Response 200:

{
"block": 8420,
"tamper_attempt": 3,
"bypass_attempt": 1
}

All /v1/admin/* endpoints require an account with the admin role.

Create a blocklist entry.

Request:

{
"domain": "gamblingsite.com",
"reason": "manual",
"category": "sports_betting",
"notes": "submitted via community report"
}

Response 201: Entry object with id.


List all blocklist entries. Query params: category, reason, page, per_page


Update a blocklist entry.


Remove a blocklist entry. Takes effect on next blocklist compilation.

Response 204: No body.


List domains pending human review (submitted via POST /v1/blocklist/report or federated reports).


POST /v1/admin/blocklist/review-queue/{domain}/resolve

Section titled “POST /v1/admin/blocklist/review-queue/{domain}/resolve”

Accept or reject a domain from the review queue.

Request:

{
"action": "approve",
"category": "casino",
"notes": "verified gambling site"
}

action values: approve, reject

Response 200: { "status": "resolved" }


List all items in the review queue.


Request: { "ids": ["01jwxyz...", "..."] }


Request: { "ids": ["01jwxyz...", "..."] }


Get a single review queue item.


Request: { "notes": "optional" }


Request: { "notes": "optional" }


Move item to deferred for later review.

Request: { "defer_until": "2026-03-22T00:00:00Z", "notes": "optional" }


App signatures are the database of gambling app identifiers (package names, bundle IDs, code signing certificates) used by the application blocking layer.

Request:

{
"platform": "android",
"identifier": "com.gambling.app",
"identifier_type": "package_name",
"display_name": "Gambling App",
"category": "casino"
}

List app signatures. Query params: platform, category, page, per_page



Full replacement of a signature record.


Response 204: No body.


Auth required: No (source IP is stripped before processing)

Submit domain reports from a self-hosted instance back to the central community feed (opt-in). Only called by the worker when BETBLOCKER_FEDERATED_REPORT_UPSTREAM is configured.

Request:

{
"api_key": "your-api-key",
"reports": [
{
"domain": "unknownsite.com",
"report_type": "heuristic_match",
"confidence": 0.87
}
]
}

Response 202: { "accepted": 1 }


Auth required: No

Returns the current list of known Tor exit node IP addresses, updated automatically by the worker. Used by agents for bypass detection.

Response 200:

{
"updated_at": "2026-03-15T04:00:00Z",
"exits": ["185.220.101.0", "..."]
}

These endpoints are only available when BB_BILLING_ENABLED=true. They are disabled on all self-hosted instances.

Start a Stripe subscription.

Returns current subscription status.

Stripe webhook receiver. Must be registered in the Stripe dashboard.

Cancel the current subscription.