Skip to main content

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.

Fastest Path to First Alert
  1. Create your query with Create Query.
  2. Start delivery with telegram, webhook, or SSE stream.
  3. Run Agent Runner so the agent can continue actions after each trigger.

Delivery Channels (Side-by-Side)

ChannelBest forSetup
WebhookProduction agent automationaction.type = "webhook" with signature verification and queue/worker processing
Telegram BotFast human-readable alertsaction.type = "telegram" for direct delivery, or webhook/SSE relay for custom formatting
SSE StreamReal-time event consumersGET /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:

  1. Receive Auto event.
  2. Verify + dedupe (eventId).
  3. Push a job to your runner queue.
  4. Let a worker decide what the agent should do next.
  5. 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

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 2xx quickly, then process asynchronously.

2) Telegram Bot Delivery

Telegram can be used as a primary delivery channel from day one.

Recommended patterns:

  1. Direct: use action.type = "telegram" in your query.
  2. Relay: receive webhook/SSE events, transform payloads, and send to Telegram Bot API.

Relay flow:

  1. Receive Auto events (webhook/SSE).
  2. Transform message payload.
  3. Send message to Telegram Bot API.

Get Telegram Bot Token

  1. Open @BotFather in Telegram.
  2. Run /newbot.
  3. Save the bot token (treat as secret).

Get Chat ID

  1. Send any message to the bot (or in a group where bot is present).
  2. Call:
curl "https://api.telegram.org/bot<YOUR_BOT_TOKEN>/getUpdates"
  1. Read message.chat.id from 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

SymptomLikely CauseFix
400 / 401 when polling or streamingMissing/invalid API key or auth headersSend x-elfa-api-key; include HMAC headers where required.
Webhook signature mismatchSigning wrong payload (not raw body) or wrong secretVerify with timestamp + "." + eventId + "." + rawBody and SHA256(secret) key.
Duplicate downstream actionsNo idempotency on event processingDeduplicate by eventId before enqueue/execute.
Event received but agent does nothingIngress processes inline and times out or failsACK quickly, push to queue, process in worker.
SSE disconnect/reconnect loopsNo retry/backoff or unstable consumerAdd reconnect backoff and heartbeat monitoring.
Missing triggers after some timeQuery expired or was cancelledPoll query status and check expiresIn, status, and last evaluations.
Signature timestamp rejectedRunner clock skewSync server clock (NTP) and enforce bounded replay window.
StageSuggested Pattern
PrototypeAuto Telegram + local worker
ProductionAuto webhook + queue + worker
Real-time operationsAuto SSE + worker service

Local vs Cloud Deployment

EnvironmentSuggested Setup
Local developmentSSE consumer + single worker process
Cloud productionWebhook ingress + queue + worker autoscaling

For full orchestration guidance, see Agent Runner. For concrete local/cloud blueprints, see Reference Implementations (Local and Cloud).