Notifications
This guide covers delivery patterns after a query triggers, with Auto as the source of truth for event emission.
For keyless x402 differences, see x402 Instructions.
- Create your query with Create Query.
- Start delivery with
telegram,webhook, or SSE stream. - Run Agent Runner so the agent can continue actions after each trigger.
Delivery Channels (Side-by-Side)
| Channel | Best for | Setup |
|---|---|---|
| Webhook | Production agent automation | action.type = "webhook" with signature verification and queue/worker processing |
| Telegram Bot | Fast human-readable alerts | action.type = "telegram" for direct delivery, or webhook/SSE relay for custom formatting |
| SSE Stream | Real-time event consumers | GET /v2/auto/queries/{queryId}/stream with API key auth |
Event Payload Contract (Canonical)
To keep webhook, Telegram relay, and SSE processing consistent, normalize incoming events into one internal contract.
Canonical event object:
{
"version": "1.0",
"eventType": "query.triggered",
"eventId": "evt_01J...",
"timestamp": "2026-04-01T12:00:00.000Z",
"queryId": "q_123",
"channel": "webhook",
"trigger": {
"symbol": "BTC",
"reason": "price > threshold"
},
"evaluation": {
"triggered": true
},
"action": {
"type": "webhook"
}
}
Webhook Event Sample
Headers:
X-Auto-Event-Id: evt_01J...
X-Auto-Signature-Timestamp: 1775035200
X-Auto-Signature: v1=<hmac_hex>
Body example:
{
"version": "1.0",
"eventType": "query.triggered",
"eventId": "evt_01J...",
"timestamp": "2026-04-01T12:00:00.000Z",
"queryId": "q_123",
"channel": "webhook",
"trigger": {
"symbol": "BTC",
"reason": "price > threshold"
},
"evaluation": {
"triggered": true
},
"action": {
"type": "webhook"
}
}
Telegram Relay Event Sample
If you use direct telegram actions, delivery can go straight to Telegram.
If you use relay mode, normalize to a Telegram job object:
{
"eventId": "evt_01J...",
"queryId": "q_123",
"channel": "telegram",
"chatId": "<CHAT_ID>",
"text": "BTC trigger fired: price > threshold",
"priority": "high"
}
SSE Event Sample
SSE frame:
event: query.triggered
id: evt_01J...
data: {"version":"1.0","eventType":"query.triggered","eventId":"evt_01J...","timestamp":"2026-04-01T12:00:00.000Z","queryId":"q_123","channel":"sse","trigger":{"symbol":"BTC","reason":"price > threshold"},"evaluation":{"triggered":true},"action":{"type":"notify"}}
Implementation note:
- Preserve the original payload for audit/debug.
- Map to the canonical contract before queueing downstream work.
Best Practice: Run a Background Orchestrator
When a condition triggers, avoid handling business logic inline in the event ingress handler.
Recommended pattern:
- Receive Auto event.
- Verify + dedupe (
eventId). - Push a job to your runner queue.
- Let a worker decide what the agent should do next.
- Execute and log outcomes.
Why this is preferred:
- Keeps ingestion fast and reliable
- Prevents duplicate downstream actions
- Makes policy and retries easier to manage
- Scales from local dev to cloud workers
1) Webhook Delivery (Recommended)
Use action.type = "webhook" and verify Auto signatures on receipt.
Signature inputs:
signing_key = SHA256(your_secret)
expected = HMAC_SHA256(signing_key, timestamp + "." + eventId + "." + rawBody)
Node.js Verification Snippet
import crypto from "crypto";
export function verifyAutoWebhook(
secret: string,
rawBody: string,
signatureHeader: string,
timestamp: string,
eventId: string,
): boolean {
if (!signatureHeader?.startsWith("v1=")) return false;
const given = signatureHeader.slice(3);
const signingKey = crypto.createHash("sha256").update(secret).digest();
const payload = `${timestamp}.${eventId}.${rawBody}`;
const expected = crypto
.createHmac("sha256", signingKey)
.update(payload)
.digest("hex");
if (given.length !== expected.length) return false;
return crypto.timingSafeEqual(Buffer.from(given), Buffer.from(expected));
}
Operational notes:
- Enforce replay window checks with
X-Auto-Signature-Timestamp. - Deduplicate by
X-Auto-Event-Id. - Return
2xxquickly, then process asynchronously.
2) Telegram Bot Delivery
Telegram can be used as a primary delivery channel from day one.
Recommended patterns:
- Direct: use
action.type = "telegram"in your query. - Relay: receive webhook/SSE events, transform payloads, and send to Telegram Bot API.
Relay flow:
- Receive Auto events (webhook/SSE).
- Transform message payload.
- Send message to Telegram Bot API.
Get Telegram Bot Token
- Open
@BotFatherin Telegram. - Run
/newbot. - Save the bot token (treat as secret).
Get Chat ID
- Send any message to the bot (or in a group where bot is present).
- Call:
curl "https://api.telegram.org/bot<YOUR_BOT_TOKEN>/getUpdates"
- Read
message.chat.idfrom the response.
Send Message
curl -X POST "https://api.telegram.org/bot<YOUR_BOT_TOKEN>/sendMessage" \
-H "Content-Type: application/json" \
-d '{
"chat_id": "<CHAT_ID>",
"text": "Auto trigger fired for BTC RSI"
}'
3) SSE Stream Delivery
Endpoint: GET /v2/auto/queries/{queryId}/stream
Required header: x-elfa-api-key: <YOUR_API_KEY>
Quick test:
curl -N "https://api.elfa.ai/v2/auto/queries/<QUERY_ID>/stream" \
-H "x-elfa-api-key: <YOUR_API_KEY>"
SSE best practices:
- Run SSE consumers on server/worker (not browser-only clients) so you can set auth headers.
- Reconnect automatically with backoff.
- Persist last seen event IDs to avoid duplicate downstream actions.
Troubleshooting
| Symptom | Likely Cause | Fix |
|---|---|---|
400 / 401 when polling or streaming | Missing/invalid API key or auth headers | Send x-elfa-api-key; include HMAC headers where required. |
| Webhook signature mismatch | Signing wrong payload (not raw body) or wrong secret | Verify with timestamp + "." + eventId + "." + rawBody and SHA256(secret) key. |
| Duplicate downstream actions | No idempotency on event processing | Deduplicate by eventId before enqueue/execute. |
| Event received but agent does nothing | Ingress processes inline and times out or fails | ACK quickly, push to queue, process in worker. |
| SSE disconnect/reconnect loops | No retry/backoff or unstable consumer | Add reconnect backoff and heartbeat monitoring. |
| Missing triggers after some time | Query expired or was cancelled | Poll query status and check expiresIn, status, and last evaluations. |
| Signature timestamp rejected | Runner clock skew | Sync server clock (NTP) and enforce bounded replay window. |
Recommended Auto-First Setup by Stage
| Stage | Suggested Pattern |
|---|---|
| Prototype | Auto Telegram + local worker |
| Production | Auto webhook + queue + worker |
| Real-time operations | Auto SSE + worker service |
Local vs Cloud Deployment
| Environment | Suggested Setup |
|---|---|
| Local development | SSE consumer + single worker process |
| Cloud production | Webhook ingress + queue + worker autoscaling |
For full orchestration guidance, see Agent Runner. For concrete local/cloud blueprints, see Reference Implementations (Local and Cloud).