AI92 நேரலையில் உள்ளது - எங்கள் உலகளாவிய மென்மையான வெளியீடு நடைபெறுகிறது: முழு தளமும் முதல் 30 நாட்களுக்கு உலகளவில் திறக்கப்பட்டுள்ளது.
ஆவணங்கள் மெனு

இந்த டெவலப்பர் ஆவணங்கள் தற்போது ஆங்கிலத்தில் மட்டுமே உள்ளன. குறியீடு மற்றும் API நடத்தை ஒவ்வொரு பிராந்தியத்திலும் ஒரே மாதிரியாக இருக்கும்.

Webhooks

Webhooks push events to your server the moment they happen — a contact gets verified, a campaign reply arrives, a visitor is identified, your credit balance runs low — so you don't have to poll. You register an HTTPS endpoint, subscribe it to event types, and AI92 POSTs a signed JSON payload to it on each matching event.

Setting up an endpoint

Register an endpoint and the event types it should receive:

bash
curl -X POST https://api.ai92.ai/v1/webhooks/endpoints \
  -H "Authorization: Bearer ai92_live_YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://yourapp.com/webhooks/ai92",
    "events": ["contact.verified", "campaign.reply", "credits.low"]
  }'

The response returns a signing secret — shown once. Store it securely; you'll use it to verify every delivery.

Event catalog

EventFires when
contact.verifiedAn email verification resolves to a definitive verdict.
campaign.sentA campaign message is dispatched.
campaign.replyA recipient replies to a campaign.
campaign.openA recipient opens a message.
campaign.clickA recipient clicks a tracked link.
campaign.bounceA message bounces.
visitor.identifiedAn anonymous visitor is identified (intent).
review.newA new review lands on a connected profile.
seo.alertAn SEO alert triggers (ranking change, issue detected).
credits.lowYour balance crosses the low-balance threshold.
key.expiringAn API key is approaching expiry.
account.inactivity_warningThe account is nearing the inactivity threshold.
account.read_onlyThe account has been moved to read-only mode.
account.reactivatedA read-only account is reactivated.
sla.violationAn SLA threshold is breached.

Only registered event types are accepted. Subscribing to an event name that is not in the catalog above returns 422 UNKNOWN_EVENT_TYPE, and the endpoint is not created — one bad name fails the whole request. In particular, async job and scan completions are not delivered via webhook: there is no job.completed or scan.completed event. Poll those results instead — GET /v1/jobs/{job_id} for f_*/strat-int-* jobs, GET /v1/seo/audit/{scan_id} and GET /v1/contacts/bulk/{scan_id} for the UUID-scan_id producers (see Async Jobs).

The payload

Every delivery is a JSON body in this shape:

json
{
  "id": "evt_8f2c1e9a-0b44-4c0d-9b2f-1a6d3e8f0011",
  "type": "contact.verified",
  "created": "2026-06-21T10:42:00Z",
  "account_id": "9b1c...",
  "trace_id": "7f3c...",
  "data": {
    "email": "[email protected]",
    "status": "valid"
  }
}

And these headers:

HeaderMeaning
X-AI92-SignatureHMAC signature: t=<timestamp>,v1=<hex>
X-AI92-Event-TypeThe event type, e.g. contact.verified
Content-Typeapplication/json
User-Agentmod923-webhook/<version>

Verifying the signature

Always verify. Without it, anyone who learns your URL could forge events. AI92 signs each delivery with HMAC-SHA256 over "{timestamp}.{raw_body}" using your endpoint's signing secret.

The X-AI92-Signature header looks like:

t=1718966520,v1=5257a869e7 ... 9f2c

To verify, step by step:

  1. Read the raw body exactly as received — do not re-serialize it. (AI92 signs a canonical, sorted-key JSON; your framework's re-encode would change the bytes and break verification. Verify against the raw bytes on the wire.)
  2. Parse X-AI92-Signature into its t (timestamp) and v1 (signature) parts.
  3. Recompute HMAC_SHA256(secret, f"{t}.{raw_body}") as hex.
  4. Compare the recomputed value with v1 using a constant-time comparison.
  5. Reject stale timestamps (e.g. t older than 5 minutes) to block replay attacks.
python
import hashlib, hmac, time

def verify_ai92_webhook(raw_body: bytes, signature_header: str, secret: str, tolerance=300) -> bool:
    parts = dict(p.split("=", 1) for p in signature_header.split(","))
    ts, sig = parts.get("t"), parts.get("v1")
    if not ts or not sig:
        return False
    if abs(time.time() - int(ts)) > tolerance:      # replay protection
        return False
    signed = f"{ts}.{raw_body.decode('utf-8')}".encode()
    expected = hmac.new(secret.encode(), signed, hashlib.sha256).hexdigest()
    return hmac.compare_digest(expected, sig)       # constant-time
javascript
import crypto from "node:crypto";

function verifyAi92Webhook(rawBody, signatureHeader, secret, tolerance = 300) {
  const parts = Object.fromEntries(signatureHeader.split(",").map((p) => p.split("=")));
  const { t, v1 } = parts;
  if (!t || !v1) return false;
  if (Math.abs(Date.now() / 1000 - Number(t)) > tolerance) return false;
  const expected = crypto
    .createHmac("sha256", secret)
    .update(`${t}.${rawBody}`)
    .digest("hex");
  return crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(v1));
}
python
# Flask handler — note request.get_data() for the RAW body
from flask import Flask, request, abort
app = Flask(__name__)

@app.post("/webhooks/ai92")
def handle():
    raw = request.get_data()  # raw bytes, not request.json
    if not verify_ai92_webhook(raw, request.headers.get("X-AI92-Signature", ""), SECRET):
        abort(400)
    event = request.get_json()
    # enqueue and return fast — see below
    enqueue(event)
    return "", 200

Retries and the DLQ

If your endpoint doesn't return a 2xx, AI92 retries with a back-off schedule:

  • Up to 5 attempts.
  • Retry intervals (seconds after each failure): 120 → 600 → 3,600 → 14,400 → 64,800 (2 min, 10 min, 1 h, 4 h, 18 h).
  • Each delivery attempt has a 10-second timeout.
  • After the final failed attempt, the delivery moves to a dead-letter queue (DLQ), retained for 30 days, where you can inspect and replay it.

A 2xx at any attempt marks the delivery complete and stops retries.

Best practices

  • Return 2xx fast. Acknowledge within the 10-second timeout, then do the real work asynchronously. Verify the signature, enqueue the event, and return 200 immediately — don't run slow logic inline.
  • Make your consumer idempotent. Retries (and rare duplicate deliveries) mean you may see the same id more than once. Dedupe on event.id and process each exactly once.
  • Verify every payload with the HMAC check above, and reject stale timestamps.
  • Subscribe narrowly. Only request the event types you handle.
  • Watch the DLQ. A delivery in the DLQ means your endpoint was down or rejecting — replay it once you've fixed the cause.