HowlAlert

API Reference

Complete reference for the HowlAlert Cloudflare Worker API endpoints.

Base URL

The API is served by a Cloudflare Worker. Your base URL will follow this pattern:

https://howlalert-api.<your-account>.workers.dev

Authentication

All protected endpoints require an Apple Identity Token passed as a Bearer token:

Authorization: Bearer <apple_identity_token>

In the development environment, the token is not cryptographically verified — any value is accepted and mapped to a synthetic user ID.

In production, the worker verifies the JWT against Apple's public keys (RSASSA-PKCS1-v1_5 / SHA-256), validates the issuer, audience (com.mrdemonwolf.howlalert), and expiration.

Requests to protected endpoints without a valid token receive a 401 Unauthorized response.

Endpoints Overview

MethodPathAuthDescription
GET/statusNoHealth check
POST/registerYesRegister device for push notifications
DELETE/registerYesDeregister a device
DELETE/device/:tokenYesUnregister device by token param
POST/eventYesSubmit a usage event
GET/preferencesYesFetch user thresholds
POST/preferencesYesUpdate user thresholds
GET/historyYesEvent history (paginated)
GET/deviceYesList registered devices
DELETE/accountYesDelete all user data

GET /status

Health check endpoint. No authentication required.

Response

{
  "status": "ok",
  "version": "0.1.0",
  "environment": "production"
}

POST /register

Register a device for push notifications.

Request Body

{
  "device_token": "string",
  "platform": "ios" | "watchos",
  "sandbox": false
}
FieldTypeRequiredDescription
device_tokenstringYesAPNs device token
platform"ios" | "watchos"YesDevice platform
sandboxbooleanNoUse APNs sandbox environment

Response

{
  "success": true,
  "message": "Device registered"
}

Example

curl -X POST https://howlalert-api.example.workers.dev/register \
  -H "Authorization: Bearer $APPLE_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "device_token": "abc123...",
    "platform": "ios",
    "sandbox": false
  }'

DELETE /register

Deregister a device by providing the device token in the request body.

Request Body

{
  "deviceToken": "string"
}

Response

{
  "message": "Device deregistered"
}

DELETE /device/:token

Unregister a device by passing the device token as a URL parameter.

Response

{
  "success": true
}

POST /event

Submit a usage event. This is the primary data-ingestion endpoint.

Request Body (UsageEventPayload)

{
  "sessionId": "string",
  "timestamp": "string",
  "model": "string",
  "inputTokens": 0,
  "outputTokens": 0,
  "cacheReadTokens": 0,
  "cacheWriteTokens": 0,
  "costUSD": 0.0
}
FieldTypeRequiredDescription
sessionIdstringYesClaude Code session identifier
timestampstringYesISO 8601 timestamp
modelstringYesModel name (e.g., claude-sonnet-4-20250514)
inputTokensnumberYesInput token count
outputTokensnumberYesOutput token count
cacheReadTokensnumberYesCache read token count
cacheWriteTokensnumberYesCache write token count
costUSDnumberYesCost in USD for this event

Response

{
  "success": true,
  "summary": {
    "totalCost": 4.32
  }
}

Example

curl -X POST https://howlalert-api.example.workers.dev/event \
  -H "Authorization: Bearer $APPLE_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "sessionId": "sess_abc123",
    "timestamp": "2026-03-17T12:00:00Z",
    "model": "claude-sonnet-4-20250514",
    "inputTokens": 1500,
    "outputTokens": 800,
    "cacheReadTokens": 200,
    "cacheWriteTokens": 100,
    "costUSD": 0.12
  }'

GET /preferences

Fetch the current user's threshold preferences.

Response

{
  "preferences": {
    "userId": "string",
    "thresholds": [
      {
        "id": "string",
        "type": "daily_cost",
        "value": 5.0,
        "isEnabled": true
      }
    ],
    "updatedAt": "2026-03-17T12:00:00Z"
  }
}

Returns { "preferences": null } if no preferences have been configured.

Types

UserPreferences

FieldTypeDescription
userIdstringUser identifier
thresholdsThresholdConfig[]Array of threshold configurations
updatedAtstringISO 8601 timestamp of last update

ThresholdConfig

FieldTypeDescription
idstringUnique threshold identifier
type"daily_cost" | "token_count" | "session_count"Threshold type
valuenumberThreshold value
isEnabledbooleanWhether this threshold is active

POST /preferences

Update the current user's threshold preferences.

Request Body

{
  "thresholds": [
    {
      "id": "string",
      "type": "daily_cost",
      "value": 10.0,
      "isEnabled": true
    }
  ]
}

Response

{
  "success": true,
  "preferences": {
    "userId": "string",
    "thresholds": [
      {
        "id": "string",
        "type": "daily_cost",
        "value": 10.0,
        "isEnabled": true
      }
    ],
    "updatedAt": "2026-03-17T12:00:00Z"
  }
}

Example

curl -X POST https://howlalert-api.example.workers.dev/preferences \
  -H "Authorization: Bearer $APPLE_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "thresholds": [
      {
        "id": "thresh_1",
        "type": "daily_cost",
        "value": 10.0,
        "isEnabled": true
      },
      {
        "id": "thresh_2",
        "type": "token_count",
        "value": 500000,
        "isEnabled": false
      }
    ]
  }'

GET /history

Retrieve paginated event history for the authenticated user.

Query Parameters

ParameterTypeDefaultDescription
limitnumber50Maximum number of events to return
offsetnumber0Number of events to skip

Response

{
  "events": [
    {
      "id": "string",
      "userId": "string",
      "sessionId": "string",
      "timestamp": "string",
      "model": "string",
      "inputTokens": 1500,
      "outputTokens": 800,
      "cacheReadTokens": 200,
      "cacheWriteTokens": 100,
      "costUSD": 0.12,
      "createdAt": "string"
    }
  ],
  "limit": 50,
  "offset": 0
}

GET /device

List all registered devices for the authenticated user.

Response

{
  "devices": [
    {
      "deviceToken": "string",
      "platform": "ios",
      "userId": "string",
      "registeredAt": "string",
      "sandbox": false
    }
  ]
}

DELETE /account

Delete all data associated with the authenticated user, including devices and event history.

Response

{
  "success": true,
  "devicesRemoved": 2,
  "eventsRemoved": 143
}

Threshold Alerting

When a usage event is submitted via POST /event, the worker performs the following steps:

  1. Inserts the event into the D1 database.
  2. Fetches the daily cost summary for the current day.
  3. Loads user preferences from KV (prefs:{userId}).
  4. Compares the daily cost against the configured threshold (defaults to $5.00 if no preferences are set).
  5. If the threshold is exceeded, sends an APNs push notification to every registered device for that user.

This check runs on every event submission, so users are alerted as soon as their spending crosses the configured limit.

Error Responses

All endpoints return standard JSON error responses.

401 Unauthorized — Missing or invalid authentication token.

{
  "error": "Unauthorized"
}

400 Bad Request — Missing required fields or invalid request body.

{
  "error": "Missing required field: device_token"
}

On this page