Skip to content
Project Hollowâ„¢Docs
Hollow AIHollowScriptAppsPlatformDocs
GuidesAPILanguage
Start hereOverviewAuthenticationConventions
Tasks APIOverviewCreate a taskWebhooksRouting rules
Lumen APIOverviewSend logsSend metricsCreate a monitorList monitorsMonitor status
Documentation

API reference

Two HTTP APIs are live today, both authenticated with the same developer key. The Tasks API puts work into Hollow from outside it. The Lumen API takes logs, metrics and uptime monitors from services you run yourself.

The rest of the suite is reachable from inside the product instead: Hollow AI’s native tools for anything you can ask for, and Cascade for anything you want to run on a schedule or a webhook.

  • Tasks API: Create tasks from a script, CI or a webhook.
  • Lumen API: Ship logs, metrics and monitors.
  • Authentication: One key, both APIs.

Authentication

Every request carries a developer key as a bearer token in the Authorization header. Mint, name and revoke keys in Settings → API keys. One key works for both APIs.

curl -X POST https://europe-west2-sstk-hollow.cloudfunctions.net/taskIntakeApi/v1/tasks \\
  -H "Authorization: Bearer hlw_sk_…" \\
  -H "Content-Type: application/json" \\
  -d '{ "text": "Investigate slow checkout" }'
  • Keys are personal. A key reaches exactly what the account that created it can reach, and no further.
  • Revoke at any time. A revoked or unknown key fails immediately with KEY_REVOKED or INVALID_KEY.
  • An account that is not approved for Hollow gets ACCOUNT_NOT_APPROVED, whatever the key.

Conventions

Shared by both APIs.

Base URLs

Each API has its own base; the version sits in the path after it, as /v1/….

Tasks
https://europe-west2-sstk-hollow.cloudfunctions.net/taskIntakeApi
Lumen
https://projecthollow.com/monitoring/ingest: a hosting rewrite in front of the same function, purely for a friendlier URL. The raw function URL, https://europe-west2-sstk-hollow.cloudfunctions.net/monitoringIngest, still works identically and is not deprecated.

Requests and responses

JSON in, JSON out. Send Content-Type: application/json on any request with a body.

Idempotency

Send an Idempotency-Key header, or an idempotencyKey field in the body on the Tasks API. Repeating a key returns the original record with "duplicate": true instead of creating a second one. On POST /v1/monitors the key goes further: it is the monitor’s identity, so a repeat is an update.

Errors

Every error body has the same shape.

{ "error": { "code": "RATE_LIMITED", "status": 429, "message": "Too many requests" } }
400
MISSING_TEXT, MISSING_FIELD, MISSING_ENTRIES, MISSING_POINTS, INVALID_FIELD, INVALID_REQUEST, TOO_MANY_ENTRIES, TOO_MANY_POINTS, MISSING_IDEMPOTENCY_KEY
401
MISSING_AUTH_HEADER, INVALID_KEY_FORMAT, INVALID_KEY, KEY_REVOKED
403
ACCOUNT_NOT_APPROVED
404
NOT_FOUND: unknown id, or not visible to this key.
409
CONFLICT: two concurrent calls shared one idempotency key and the first has not finished. Retry.
413
PAYLOAD_TOO_LARGE: the body is over 256 KiB.
429
RATE_LIMITED or QUOTA_EXCEEDED.

Rate limits and quotas

Tasks
120 requests per minute per key, and 2,000 tasks a day per key.
Lumen
600 requests per minute per key, and 100,000 events a day per key, logs and metrics counted together.

The two limiters are separate counters, so a busy ingestion integration cannot eat into your task-creation budget.

Tasks API

Tasks

Put work into Hollow from anywhere: a script, CI, a form, another product. Two front doors on the same intake spine: a developer key for code you write, and a webhook URL for services you cannot.

On this pageCreateWebhooksRouting
POST/v1/tasks

Create one task, synchronously. text is the only required field; everything else narrows where it lands.

text
The title, up to 500 characters. title is accepted as an alias. Required.
description
Plain text. Optional.
listId, listName
The explicit destination list. Optional, and the clearest way to target one.
list
A list id or name, whichever you have. Optional.
priority
low · medium · high · urgent. Optional.
labels
An array of strings. Optional.
dueDate, dueTime
YYYY-MM-DD and HH:mm. Optional.
idempotencyKey
Also accepted as the Idempotency-Key header. Optional.

Request

curl -X POST https://europe-west2-sstk-hollow.cloudfunctions.net/taskIntakeApi/v1/tasks \\
  -H "Authorization: Bearer hlw_sk_…" \\
  -H "Content-Type: application/json" \\
  -d '{
    "text": "Investigate slow checkout",
    "description": "p95 latency doubled after the deploy",
    "list": "Engineering",
    "priority": "high",
    "labels": ["perf"],
    "idempotencyKey": "deploy-1234"
  }'
201
Created. The body carries id, ticketKey and listId, plus misrouted: true if your target list no longer existed and the task fell back to a default one.
200
{ "id", "duplicate": true } for an idempotent repeat.

An unmatched list name falls back to the default list of your default board. Lists are never created for you.

POST/hook/{hookId}

A no-code URL you paste into GitHub, Zapier or anything else that can call one. Create it in Tasks → Integrations: pick the destination list, map which JSON paths hold the title, description, priority and event id, and add routing rules if you want them.

Endpoint

https://europe-west2-sstk-hollow.cloudfunctions.net/taskIntakeHook/hook/{hookId}?token=hook_…

The URL embeds an unguessable token, shown once. That token is the authentication, and you can rotate it at any time. It can be sent as an X-Hollow-Token header instead of a query parameter.

title
issue.title
description
issue.body
event id
issue.id: the dedupe key

GitHub redelivers events. Because the mapping picks out an event id, a redelivery returns the original task rather than creating a second one.

sync
The default. The task is created immediately and the response carries its id and ticket key.
queue
For very high-volume sources: the request is staged and promoted asynchronously, exactly once, and returns 202. Use it only if a burst would otherwise contend on your task document.

Routing rules

Both front doors take an ordered list of routing rules. Each rule’s condition is a HEL predicate (the same query language Tasks filtering uses) evaluated against the normalised ticket. First match wins; if nothing matches, the channel’s default destination applies.

The fields a rule can read are title, description, priority, label, emaildomain and source.

title:billing OR title:invoice        → a "Finance" list
priority>=high emaildomain:acme.com   → a "VIP" list
Lumen API

Lumen

Send logs, metrics and uptime monitors from services you run yourself, and read a monitor’s state back. Same developer key as the Tasks API.

On this pageLogsMetricsMonitorsListStatus
POST/v1/logs

Batched log ingestion. Up to 500 entries per request, inside a 256 KiB body.

Request

curl -X POST https://projecthollow.com/monitoring/ingest/v1/logs \\
  -H "Authorization: Bearer hlw_sk_…" \\
  -H "Content-Type: application/json" \\
  -d '{
    "service": "checkout-api",
    "entries": [
      { "level": "error", "message": "party lookup timed out", "ts": "2026-07-26T09:14:02Z" }
    ]
  }'

Response · 202

{ "accepted": 1 }
POST/v1/metrics

Batched metric points. Up to 1,000 points per request, inside a 256 KiB body.

Request

curl -X POST https://projecthollow.com/monitoring/ingest/v1/metrics \\
  -H "Authorization: Bearer hlw_sk_…" \\
  -H "Content-Type: application/json" \\
  -d '{
    "service": "checkout-api",
    "points": [
      { "name": "request_latency_ms", "value": 214, "ts": "2026-07-26T09:14:02Z",
        "tags": { "route": "/checkout/start" } }
    ]
  }'

Response · 202

{ "accepted": 1 }
POST/v1/monitors

Create or update a monitor as code. An Idempotency-Key is required, as a header or a body field, and here it is the monitor’s identity rather than a replay guard: the first call with a given key creates a monitor, and every later call with that key applies the declaration to the same one. Changing intervalMinutes or target in a config file and re-running is an update, never a second monitor.

Request

curl -X POST https://projecthollow.com/monitoring/ingest/v1/monitors \\
  -H "Authorization: Bearer hlw_sk_…" \\
  -H "Content-Type: application/json" \\
  -H "Idempotency-Key: checkout-api-uptime" \\
  -d '{
    "name": "Checkout API uptime",
    "type": "http",
    "target": "https://checkout.example.com/health",
    "intervalMinutes": 5
  }'
201
Created. { "id": "mon_abc123" }
200
Updated in place, on a repeat with the same key. { "id": "mon_abc123", "duplicate": true }

A browser monitor also takes a steps array (at most 20 entries of kind, selector, optional value and timeoutMs, where kind is click, fill, wait_for or assert_text) and is rejected without one. Validation is identical on create and update, so a repeat with a bad target fails with INVALID_REQUEST rather than silently keeping the old value.

GET/v1/monitors

Every monitor belonging to the key’s owner.

Response · 200

{
  "monitors": [
    { "id": "mon_abc123", "name": "Checkout API uptime", "type": "http",
      "target": "https://checkout.example.com/health", "status": "up",
      "intervalMinutes": 5 }
  ]
}
GET/v1/monitors/{id}/status

The current state of one monitor, and when it was last checked.

Response · 200

{ "id": "mon_abc123", "status": "up", "lastCheckedAt": "2026-07-26T09:14:02Z" }

Project Hollow

Hollow AIAppsPlatform

Documentation

OverviewGetting startedHollow StudioApp reference

Account

Sign inCreate accountEarly access

Legal

PrivacyTermsContact
© 2026 Project Hollow