---
title: "Agent-Created Database Alerts - Documentation"
description: "Let an AI agent inspect a database, preview eligible rows, and safely publish a scheduled authenticated webhook alert."
canonical: "https://www.aifordatabase.com/docs/agent-created-alerts/"
---

# Agent-Created Database Alerts

advanced 10 min read

## Agent-Created Database Alerts

An AI agent can create a complete database-to-webhook alert using only the public REST API. This recipe sends newly eligible users to Brevo as custom events without adding a sent flag to the connected database or maintaining a Brevo list.

### Required API-key permissions

Create an admin-owned `afd_` key with `connections`, `query`, `workflows`, and `workflow_credentials`. Connection passwords are never returned. Outbound workflow credentials are write-only, encrypted, and restricted to explicitly allowed destination hosts.

### 1\. Discover and test the source

```http
GET /api/v1/connections
GET /api/v1/connections/{connectionId}
GET /api/v1/connections/{connectionId}/schema
```

Connection reads return sanitized metadata, health state, and schema state—never the database password. The schema endpoint returns tables and columns.

Run SQL directly and receive raw `columns`, `rows`, `rowCount`, and `executionTime`:

```http
POST /api/v1/connections/{connectionId}/query
Content-Type: application/json

{
  "sql": "SELECT id, name, email, \"createdAt\" FROM \"User\" ORDER BY \"createdAt\" DESC LIMIT 5",
  "question": "Who signed up most recently?"
}
```

`question` is optional: the question the query answers, in the user's words (up to 2,000 characters). It is stored with the query and shown in the app's query history. The AI connector passes it for queries Claude or ChatGPT run.

For a 15-minute alert selecting users as they cross the three-hour signup mark, PostgreSQL can shape each row as a Brevo event:

```sql
SELECT
  'aifd_signup_eligible' AS event_name,
  jsonb_build_object('email_id', email, 'ext_id', id) AS identifiers,
  jsonb_build_object('FIRSTNAME', split_part(name, ' ', 1)) AS contact_properties,
  jsonb_build_object('signed_up_at', "createdAt") AS event_properties
FROM "User"
WHERE "createdAt" >= NOW() - INTERVAL '3 hours 15 minutes'
  AND "createdAt" < NOW() - INTERVAL '3 hours'
ORDER BY "createdAt";
```

Adjust the identifiers to the connected schema. Each scheduled run covers a different non-overlapping signup window.

### 2\. Store the Brevo API key

```http
POST /api/v1/workflow-credentials
Content-Type: application/json

{
  "name": "Brevo events",
  "kind": "API_KEY_HEADER",
  "allowedHosts": ["api.brevo.com"],
  "config": {
    "headerName": "api-key",
    "value": "xkeysib-your-current-brevo-key"
  }
}
```

Save the returned credential `id`. The secret is not present in this or any later response.

### 3\. Create a paused workflow

```json
{
  "name": "AIFD signup lifecycle event",
  "description": "Send users crossing the three-hour signup mark to Brevo",
  "connectionId": "YOUR_CONNECTION_ID",
  "triggerType": "SCHEDULE",
  "triggerConfig": {
    "preset": "EVERY_N_MINUTES",
    "minutes": 15
  },
  "steps": [
    {
      "name": "Eligible signups",
      "query": "SELECT 'aifd_signup_eligible' AS event_name, jsonb_build_object('email_id', email, 'ext_id', id) AS identifiers, jsonb_build_object('FIRSTNAME', split_part(name, ' ', 1)) AS contact_properties, jsonb_build_object('signed_up_at', \"createdAt\") AS event_properties FROM \"User\" WHERE \"createdAt\" >= NOW() - INTERVAL '3 hours 15 minutes' AND \"createdAt\" < NOW() - INTERVAL '3 hours' ORDER BY \"createdAt\"",
      "stopIfEmpty": true
    }
  ],
  "actions": [
    {
      "type": "WEBHOOK",
      "config": {
        "version": 2,
        "url": "https://api.brevo.com/v3/events/batch",
        "method": "POST",
        "credentialId": "YOUR_WORKFLOW_CREDENTIAL_ID",
        "headers": [],
        "bodyMode": "JSON",
        "bodyTemplate": "{\"events\":{{query_1.rows}}}",
        "timeoutMs": 10000,
        "retry": {
          "maxAttempts": 3,
          "baseDelayMs": 500
        },
        "successStatusCodes": [202],
        "critical": true
      }
    }
  ]
}
```

Send that body to `POST /api/v1/workflows`. Creation always returns a paused draft. The unquoted `{{query_1.rows}}` placeholder preserves the database result as a JSON array inside Brevo's required `events` envelope. `successStatusCodes: [202]` makes Brevo's HTTP 207 partial-acceptance response a visible failure instead of a false success.

### 4\. Preview, test, and publish

Preview only the draft queries:

```http
POST /api/v1/workflows/{workflowId}/preview
```

Preview returns up to 100 raw rows per step, condition state, and `wouldRunActions`. It never contacts Brevo, creates a `WorkflowRun`, or persists the returned preview rows.

After reviewing the recipients, send one controlled live request:

```http
POST /api/v1/workflows/{workflowId}/actions/0/test
Content-Type: application/json

{
  "confirmDelivery": true
}
```

Inspect the delivery attempts and HTTP response. A fully accepted Brevo batch returns 202. Finally publish:

```http
PATCH /api/v1/workflows/{workflowId}
Content-Type: application/json

{
  "isActive": true
}
```

If publishing returns warnings, review them and resend their exact strings in `acknowledgedWarnings`. Scheduled runs use an immutable published version.

### Brevo automation

Trigger the Brevo automation on `aifd_signup_eligible`. Send the welcome email immediately, wait 21 hours, and send the check-in email. Keep **Allow contact re-entry after exit** disabled so the same contact cannot receive this welcome sequence twice.

No Brevo list or source-database update is required. Brevo still keeps its normal contact and automation state. Time-window queries prevent repeats during normal scheduling; Brevo's no-re-entry setting is the final duplicate-email boundary.

Source: https://www.aifordatabase.com/docs/agent-created-alerts/
