# AyeWatch API — Complete Documentation

Every page of the AyeWatch API reference, concatenated.

Source: https://ayewatch.ai/documentation
Generated: 2026-08-08T10:54:28.459Z

## Contents

- [AyeWatch API](https://ayewatch.ai/documentation) — Monitor Internet Information and receive real-time webhook events via the AyeWatch REST API.
- [Authentication](https://ayewatch.ai/documentation/authentication) — All API requests must be authenticated with a Bearer API key.
- [Topics API](https://ayewatch.ai/documentation/topics) — Create and manage monitoring topics via REST. All endpoints require a Bearer API key.
- [Webhooks](https://ayewatch.ai/documentation/webhooks) — Receive real-time events when AyeWatch detects changes in your monitored topics.
- [Errors](https://ayewatch.ai/documentation/errors) — Standard error format and HTTP status codes returned by the AyeWatch API.

---

# AyeWatch API

Monitor Internet Information and receive real-time webhook events via the AyeWatch REST API.

Source: https://ayewatch.ai/documentation

Base URL: `https://ayewatch.ai/api/v1`

## What is the AyeWatch API?

The AyeWatch API lets you programmatically manage monitoring internet information topics and
receive real-time notifications when content changes are detected. Instead of manually checking
for updates, you define topics to monitor and AyeWatch delivers structured webhook events to your
application whenever something new happens.

- Create, list, update, and delete monitoring topics via REST
- Receive `topic.update` webhook events with AI-generated summaries
- Verify webhook signatures with HMAC-SHA256
- Authenticate all requests with a Bearer API key

## Quick Start

Get started in two steps: create a topic to monitor, then set up a webhook to receive updates.

### Step 1 — Create a topic (monitors a URL)

Request:

```bash
curl -X POST https://ayewatch.ai/api/v1/topics \
  -H "Authorization: Bearer aw_live_YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "https://openai.com/news",
    "interval": "1_hour"
  }'
```

Response — 201 Created:

```json
{
  "data": {
    "id": 1042,
    "name": "https://openai.com/news",
    "topic_type": "web_page",
    "is_active": true,
    "interval": "1_hour",
    "schedule_arn": "arn:aws:scheduler:...:schedule/default/topic-1042",
    "next_run_at": null,
    "created_at": "2026-08-03T12:00:00Z",
    "schedule_status": "synced"
  }
}
```

### Step 2 — Configure your webhook endpoint

Set your webhook URL in the [Developer dashboard](https://ayewatch.ai/developer/webhook). AyeWatch will POST
events to your endpoint whenever a topic is checked.

Example webhook payload:

```json
{
  "topic_id": 1042,
  "topic_name": "https://openai.com/news",
  "headline": "GPT-5 announced",
  "content": "OpenAI today announced GPT-5...",
  "created_at": "2026-03-08T13:00:00Z"
}
```

## Explore the Docs

- [Authentication](https://ayewatch.ai/documentation/authentication) — API key format, Bearer headers, and security tips.
- [Topics API](https://ayewatch.ai/documentation/topics) — Create, list, update, and delete monitoring topics.
- [Webhooks](https://ayewatch.ai/documentation/webhooks) — Event format, HMAC signature verification, and retry behavior.
- [Errors](https://ayewatch.ai/documentation/errors) — HTTP status codes and standard error response shape.

## Markdown for LLMs and Agents

Every page in these docs is also served as plain markdown. Append `.md` to any documentation URL
(for example `https://ayewatch.ai/documentation/topics.md`), or request the HTML URL with an
`Accept: text/markdown` header. The complete reference in one file is at
`https://ayewatch.ai/documentation/all.md`, and site-wide summaries live at `https://ayewatch.ai/llms.txt`
and `https://ayewatch.ai/llms-full.txt`.

---

# Authentication

All API requests must be authenticated with a Bearer API key.

Source: https://ayewatch.ai/documentation/authentication

## Generating API Keys

API keys are available on paid plans. Generate and manage your keys in the [Developer dashboard](https://ayewatch.ai/developer).
You can create up to the limit for your plan and revoke them at any time.

> **Plan requirement:** API keys are only available on paid plans. Upgrade your plan to enable API
> access.

## Rate Limits

Each API key is limited to 120 requests per minute by default. Every authenticated response includes
`RateLimit-Limit`, `RateLimit-Remaining`, `RateLimit-Reset`, and `X-Request-Id` headers. A 429
response also includes `Retry-After`.

## Key Format

All AyeWatch API keys follow this format:

```text
aw_live_<96 hex characters>
```

Example: `aw_live_a1b2c3d4e5f6...` (total length: ~104 characters)

## Making Authenticated Requests

Pass your API key in the `Authorization` header as a Bearer token:

```bash
curl https://ayewatch.ai/api/v1/topics \
  -H "Authorization: Bearer aw_live_YOUR_API_KEY"
```

## Security Tips

- **Never expose keys client-side.** API keys grant full access to your topics. Keep them in
  environment variables or secrets managers on your server.
- **Rotate keys regularly.** Revoke and regenerate keys periodically, or immediately if you suspect
  a leak.
- **Use one key per integration.** Create separate keys for different services so you can revoke
  individual ones without affecting others.
- **Never commit keys to source control.** Use `.env` files and add them to `.gitignore`.

## Error Responses

Missing or invalid API keys return a `401 Unauthorized` response:

```json
{
  "error": {
    "message": "Unauthorized",
    "status": 401
  }
}
```

See the [Errors reference](https://ayewatch.ai/documentation/errors) for all status codes.

---

# Topics API

Create and manage monitoring topics via REST. All endpoints require a Bearer API key.

Source: https://ayewatch.ai/documentation/topics

## Scope

These endpoints see only the topics created through the API. Topics you created in the AyeWatch app
aren't returned by the list endpoint, and requesting one by ID returns `404`. Updates for
API-created topics are delivered by [webhook](https://ayewatch.ai/documentation/webhooks) — configure an
endpoint or those updates have nowhere to go.

## Topic types (auto-detected from `name`)

- `web_page` — if `name` is a valid HTTP or HTTPS URL, AyeWatch monitors that specific page.
- `subject` — if `name` is plain text, AyeWatch monitors that keyword/subject across the internet.

## Endpoints

| Method | Path | Description |
| --- | --- | --- |
| `POST` | `/api/v1/topics` | Create a topic |
| `GET` | `/api/v1/topics` | List topics (paginated) |
| `GET` | `/api/v1/topics/:id` | Get a topic |
| `PUT` | `/api/v1/topics/:id` | Update a topic |
| `DELETE` | `/api/v1/topics/:id` | Delete a topic |

## POST /api/v1/topics

Create a new monitoring topic.

Active topics are limited by your plan's monitoring capacity. Accounts also have a default storage
limit of 3,000 topics, counting every topic you have — active or paused, created through the API or
in the app.

### Request Body (JSON)

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| `name` | string | Yes | A valid URL (e.g. "https://openai.com/news") to monitor a page, or plain text (e.g. "OpenAI announcements") to monitor a subject. Topic type is auto-detected. |
| `interval` | string | Yes | Check interval. One of: "0_5_hours", "1_hour", "6_hours", "12_hours", "1_day", "2_days", "3_5_days", "7_days", "14_days", "30_days", "182_days", "365_days". Your plan must unlock the interval you choose. |
| `description` | string | No | What should alert you. Describe the change you care about, e.g. "Tell me when a new AI model drops." |
| `is_active` | boolean | No | Whether to start monitoring immediately. Default: true. |
| `verification_level` | string | No | How much corroboration is required before you're alerted: "instant", "balanced", or "verified". Defaults to "instant" for pages and "balanced" for subjects. |
| `triggers` | array | No | Precise conditions to alert on, instead of relying on the description alone. See Triggers below. Max 4. |

### Triggers (optional)

A trigger states a condition in structured form. When you supply them, AyeWatch still falls back to
interpreting your `description` for anything the triggers don't cover.

- `entity` — what is being watched, e.g. "GPT-5".
- `attribute` — the property of it, e.g. "price".
- `op` — one of `<`, `<=`, `>`, `>=`, `=`, `changed`, `crossed_above`, `crossed_below`.
- `value` — must be numeric for the comparison operators (everything except `changed`).
- `source_url` — optional, restricts the trigger to one source.

Example — alert on a precise condition:

```bash
curl -X POST https://ayewatch.ai/api/v1/topics \
  -H "Authorization: Bearer aw_live_YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "NVIDIA H100 pricing",
    "interval": "6_hours",
    "verification_level": "verified",
    "triggers": [
      {
        "entity": "H100 80GB",
        "attribute": "price",
        "op": "<",
        "value": 25000,
        "value_type": "number"
      }
    ]
  }'
```

Example — monitor a URL:

```bash
curl -X POST https://ayewatch.ai/api/v1/topics \
  -H "Authorization: Bearer aw_live_YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "https://openai.com/news",
    "interval": "1_hour"
  }'
```

Example — monitor a subject / keyword:

```bash
curl -X POST https://ayewatch.ai/api/v1/topics \
  -H "Authorization: Bearer aw_live_YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "OpenAI product announcements",
    "interval": "1_day"
  }'
```

Response — 201 Created:

```json
{
  "data": {
    "id": 1042,
    "name": "https://openai.com/news",
    "description": null,
    "is_active": true,
    "interval": "1_hour",
    "topic_type": "web_page",
    "schedule_arn": "arn:aws:scheduler:...:schedule/default/topic-1042",
    "next_run_at": null,
    "created_at": "2026-08-03T12:00:00Z",
    "schedule_status": "synced"
  }
}
```

### About `schedule_status`

Returned on create, update, and delete. The topic itself is already saved when you get a `2xx` —
this field only tells you whether its monitoring schedule has finished being applied.

- `synced` — the schedule is live. Nothing more to do.
- `pending` — the change is queued and will be applied automatically within a few minutes. This is
  not an error and does not need a retry; the topic is saved either way.

## GET /api/v1/topics

List all API-created topics for the authenticated user, paginated.

### Query Parameters

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| `page` | number | No | Page number. Default: 1. |
| `page_size` | number | No | Results per page. Max: 100. Default: 20. |
| `is_active` | boolean | No | Filter by active status. Pass "true" or "false". |
| `interval` | string | No | Filter by interval. One of: "1_hour", "6_hours", etc. Legacy values on older topics are also accepted as a filter. |
| `created_after` | string (ISO 8601) | No | Only topics created after this datetime. |
| `created_before` | string (ISO 8601) | No | Only topics created before this datetime. |

Request:

```bash
curl "https://ayewatch.ai/api/v1/topics?page=1&page_size=10&is_active=true" \
  -H "Authorization: Bearer aw_live_YOUR_API_KEY"
```

Response — 200 OK:

```json
{
  "data": [
    {
      "id": 1042,
      "name": "https://openai.com/news",
      "topic_type": "web_page",
      "description": null,
      "is_active": true,
      "interval": "1_hour",
      "schedule_arn": "arn:aws:scheduler:...:schedule/default/topic-1042",
      "next_run_at": null,
      "created_at": "2026-03-08T12:00:00Z"
    }
  ],
  "pagination": {
    "page": 1,
    "page_size": 10,
    "has_more": false
  }
}
```

## GET /api/v1/topics/:id

Retrieve a single topic by ID.

Request:

```bash
curl https://ayewatch.ai/api/v1/topics/1042 \
  -H "Authorization: Bearer aw_live_YOUR_API_KEY"
```

Response — 200 OK:

```json
{
  "data": {
    "id": 1042,
    "name": "https://openai.com/news",
    "topic_type": "web_page",
    "description": null,
    "is_active": true,
    "interval": "1_hour",
    "schedule_arn": "arn:aws:scheduler:...:schedule/default/topic-1042",
    "next_run_at": null,
    "created_at": "2026-03-08T12:00:00Z"
  }
}
```

## PUT /api/v1/topics/:id

Update a topic. Send only the fields you want to change.

### Request Body (JSON) — all fields optional

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| `name` | string | No | New name or URL. Topic type is re-detected automatically. |
| `description` | string | No | New description. |
| `interval` | string | No | New check interval. One of: "1_hour", "6_hours", etc. Your plan must unlock the interval you choose. |
| `is_active` | boolean | No | Enable or disable the topic. |
| `verification_level` | string | No | New verification level: "instant", "balanced", or "verified". |
| `triggers` | array | No | Replaces the existing triggers. Send an empty array to clear them; omit the field to leave them untouched. |

The request must contain at least one of these fields. Unknown fields are rejected with a `400`
rather than silently ignored.

Request:

```bash
curl -X PUT https://ayewatch.ai/api/v1/topics/1042 \
  -H "Authorization: Bearer aw_live_YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"interval": "6_hours", "is_active": false}'
```

Response — 200 OK:

```json
{
  "data": {
    "id": 1042,
    "name": "https://openai.com/news",
    "description": null,
    "is_active": false,
    "interval": "6_hours",
    "topic_type": "web_page",
    "schedule_arn": null,
    "next_run_at": null,
    "created_at": "2026-03-08T12:00:00Z",
    "schedule_status": "synced"
  }
}
```

## DELETE /api/v1/topics/:id

Permanently delete a topic and its schedule. This action cannot be undone.

Request:

```bash
curl -X DELETE https://ayewatch.ai/api/v1/topics/1042 \
  -H "Authorization: Bearer aw_live_YOUR_API_KEY"
```

Response — 200 OK:

```json
{
  "data": {
    "deleted": true,
    "schedule_status": "synced"
  }
}
```

---

# Webhooks

Receive real-time events when AyeWatch detects changes in your monitored topics.

Source: https://ayewatch.ai/documentation/webhooks

## Setup

Configure your webhook endpoint URL in the [Developer dashboard → Webhook](https://ayewatch.ai/developer/webhook).
Your endpoint must:

- Be publicly accessible over **HTTPS**
- Respond with a `2xx` status within **10 seconds**
- Accept `POST` requests with a JSON body

> **Note:** Failed deliveries are retried with exponential backoff for up to 10 attempts. Make
> handlers idempotent because a delivery may be attempted more than once.

## When Webhooks Fire

AyeWatch sends a webhook only when new content is detected for a monitored topic. No request is sent
when a topic is checked but nothing has changed.

## Webhook Payload

AyeWatch sends a `POST` request with a JSON body to your endpoint:

```json
{
  "topic_id": 1042,
  "topic_name": "https://openai.com/news",
  "update_id": 7891,
  "headline": "GPT-5 announced",
  "content": "OpenAI today announced the release of GPT-5...",
  "content_detail": "...",
  "created_at": "2026-03-08T12:00:00Z"
}
```

| Field | Type | Description |
| --- | --- | --- |
| `topic_id` | number | Integer ID of the monitored topic (matches the id from the Topics API). |
| `topic_name` | string | Name or URL of the topic as set on creation. |
| `update_id` | number | Integer ID of this specific update. Use it to deduplicate retried deliveries. |
| `headline` | string \| undefined | Short AI-generated summary of the detected change. |
| `content` | string \| undefined | Full content of the update. |
| `content_detail` | string \| undefined | Additional detail about the update. |
| `created_at` | string (ISO 8601) | Timestamp when the update was created. |
| `novelty_class` | string \| undefined | How new this is relative to what was already reported on the topic. |
| `score` | number \| undefined | Internal confidence in the update's significance. |
| `evidence` | array \| undefined | Supporting quotes with their source URLs: [{ quote, source_url }]. |
| `trigger` | string \| undefined | Which of the topic's triggers fired, when the update came from one. |

## Signature Verification

Every webhook request includes an `X-AyeWatch-Signature` header containing an HMAC-SHA256 signature
signed with your webhook secret. Verify against the raw request bytes — not a re-serialized version
of the parsed payload.

Header format: `X-AyeWatch-Signature: sha256=<hex_digest>`

Always verify this signature before processing webhook events. Reject events that fail verification.

Node.js / Express:

```js
const crypto = require("crypto");

function verifyWebhookSignature(rawBody, signature, secret) {
  const expected = "sha256=" + crypto
    .createHmac("sha256", secret)
    .update(rawBody)
    .digest("hex");

  // Use timingSafeEqual to prevent timing attacks
  const expectedBuffer = Buffer.from(expected, "utf8");
  const signatureBuffer = Buffer.from(signature, "utf8");

  if (expectedBuffer.length !== signatureBuffer.length) return false;
  return crypto.timingSafeEqual(expectedBuffer, signatureBuffer);
}

// Express example
app.post("/webhook", express.raw({ type: "application/json" }), (req, res) => {
  const signature = req.headers["x-ayewatch-signature"];
  const isValid = verifyWebhookSignature(req.body, signature, process.env.WEBHOOK_SECRET);

  if (!isValid) {
    return res.status(401).json({ error: "Invalid signature" });
  }

  const event = JSON.parse(req.body);
  console.log("Update received for topic:", event.topic_id, event.headline);
  res.status(200).json({ received: true });
});
```

Python / FastAPI:

```python
import hashlib
import hmac

def verify_webhook_signature(raw_body: bytes, signature: str, secret: str) -> bool:
    expected = "sha256=" + hmac.new(
        secret.encode("utf-8"),
        raw_body,
        hashlib.sha256
    ).hexdigest()
    return hmac.compare_digest(expected, signature)

# FastAPI example
from fastapi import FastAPI, Request, HTTPException

app = FastAPI()

@app.post("/webhook")
async def webhook(request: Request):
    raw_body = await request.body()
    signature = request.headers.get("x-ayewatch-signature", "")

    if not verify_webhook_signature(raw_body, signature, WEBHOOK_SECRET):
        raise HTTPException(status_code=401, detail="Invalid signature")

    event = await request.json()
    print("Update received for topic:", event["topic_id"], event.get("headline"))
    return {"received": True}
```

## Delivery Behavior

- **Retries:** Non-2xx responses and timeouts are retried with exponential backoff, up to 10
  attempts.
- **10-second timeout:** Requests that don't complete within 10 seconds are abandoned.
- **HTTPS only:** Plain HTTP endpoints are not supported.
- **Order not guaranteed:** Events may arrive out of order under load.
- **Additive payloads:** New fields may be added over time. Ignore any you don't recognise rather
  than rejecting the request.

## Rotating Your Secret

You can rotate the signing secret from the [Developer dashboard → Webhook](https://ayewatch.ai/developer/webhook).
Rotation takes effect immediately — there is no overlap window, so deliveries signed with the new
secret will fail verification until your receiver is updated. Deploy the new secret first, or rotate
during a window where a few retried deliveries are acceptable.

---

# Errors

Standard error format and HTTP status codes returned by the AyeWatch API.

Source: https://ayewatch.ai/documentation/errors

## Error Response Shape

All API errors return a consistent JSON structure:

```json
{
  "error": {
    "message": "Human-readable error description",
    "status": 400
  }
}
```

The `status` field mirrors the HTTP response status code. Always check the HTTP status first, then
read `error.message` for details.

## HTTP Status Codes

| Status | Name | When it occurs |
| --- | --- | --- |
| `400` | Bad Request | The request body or parameters are invalid or missing required fields. Also returned when the interval you asked for isn't unlocked by your plan. |
| `401` | Unauthorized | API key is missing, malformed, or does not exist. |
| `403` | Forbidden | The API key is valid, but the account is on Free Preview, or the request would exceed a plan limit such as active topics or monitoring capacity. |
| `404` | Not Found | The requested resource (topic ID) does not exist or belongs to another user. |
| `409` | Conflict | The request conflicts with your existing topics — either a topic with that name already exists, or another change to your topics was still being applied. The second case is safe to retry after a short pause. |
| `429` | Too Many Requests | Rate limit exceeded for this API key. Wait for the number of seconds given in the Retry-After header before retrying. |
| `500` | Internal Server Error | An unexpected error occurred on our side. Please retry or contact support. |

## Example Error Responses

401 — Missing or invalid API key:

```json
{
  "error": {
    "message": "Unauthorized",
    "status": 401
  }
}
```

403 — Upgrade required:

```json
{
  "error": {
    "message": "API access requires a paid plan",
    "status": 403
  }
}
```

403 — Max API keys reached:

```json
{
  "error": {
    "message": "You have reached the maximum of 10 active API keys",
    "status": 403
  }
}
```

403 — Plan limit reached:

```json
{
  "error": {
    "message": "Not enough monitoring capacity for Hourly right now. Slow or remove a topic, or upgrade.",
    "status": 403
  }
}
```

404 — Topic not found:

```json
{
  "error": {
    "message": "Topic not found",
    "status": 404
  }
}
```

409 — Duplicate topic name:

```json
{
  "error": {
    "message": "A topic with this name already exists",
    "status": 409
  }
}
```

409 — Another change was in progress (retry):

```json
{
  "error": {
    "message": "Timed out waiting for another monitor change to finish.",
    "status": 409
  }
}
```

## Need Help?

If you encounter a persistent 500 error or unexpected behavior, please [contact support](https://ayewatch.ai/contact)
with the request details and timestamps.

---
