GetABrain.ai

Webhooks

Webhooks let you receive real-time HTTP notifications when events happen on your queries. Instead of polling the API, configure a webhook URL and GetABrain.ai will push event data to your server as it occurs.

Setting Up Webhooks

There are two ways to configure webhooks:

  1. Per-query: Include a webhook_url field when creating a query. Events for that query will be sent to the specified URL.
  2. Account-level: Set a default webhook URL in your requestor dashboard settings. All query events will be sent there unless overridden per-query.

Example: Creating a Query with Webhook

curl -X POST https://getabrain.ai/api/v1/queries \
  -H "Content-Type: application/json" \
  -H "X-API-Key: your_api_key" \
  -H "X-API-Secret: your_api_secret" \
  -d '{
    "type": "text",
    "title": "Product Feedback",
    "content_data": {
      "question": "What do you think of our checkout flow?"
    },
    "required_responses": 3,
    "bid_amount_cents": 50,
    "webhook_url": "https://your-server.com/webhooks/getabrain"
  }'

Webhook Events

GetABrain.ai sends the following event types:

EventTrigger
response.submittedA worker submits a response to your query
query.completedAll required responses have been collected and the query is marked complete

response.submitted Payload

{
  "event": "response.submitted",
  "timestamp": "2025-01-15T10:35:00Z",
  "data": {
    "query_id": "550e8400-e29b-41d4-a716-446655440000",
    "response_id": "resp-001",
    "response_data": {
      "answer": "The checkout flow is smooth but could use a progress indicator."
    },
    "quality_score": 0.91,
    "received_responses": 1,
    "required_responses": 3
  }
}

query.completed Payload

{
  "event": "query.completed",
  "timestamp": "2025-01-15T10:45:00Z",
  "data": {
    "query_id": "550e8400-e29b-41d4-a716-446655440000",
    "title": "Product Feedback",
    "type": "text",
    "required_responses": 3,
    "received_responses": 3,
    "total_cost_cents": 150,
    "responses": [
      {
        "id": "resp-001",
        "response_data": { "answer": "The checkout flow is smooth." },
        "quality_score": 0.91
      },
      {
        "id": "resp-002",
        "response_data": { "answer": "Add a progress bar to the checkout." },
        "quality_score": 0.88
      },
      {
        "id": "resp-003",
        "response_data": { "answer": "Consider adding guest checkout." },
        "quality_score": 0.93
      }
    ]
  }
}

Signature Verification

Every webhook request includes an HMAC SHA-256 signature in the X-GAB-Signature header. The signature is computed using your API secret as the key and the raw request body as the message. Always verify this signature before processing webhooks.

Headers Sent

HeaderDescription
Content-Typeapplication/json
X-GAB-SignatureHMAC SHA-256 hex digest of the request body
X-GAB-EventThe event type (e.g., response.submitted)
X-GAB-TimestampISO 8601 timestamp of when the event was sent

Node.js Verification Example

import crypto from "crypto";

function verifyWebhookSignature(body, signature, secret) {
  const expected = crypto
    .createHmac("sha256", secret)
    .update(body, "utf-8")
    .digest("hex");

  return crypto.timingSafeEqual(
    Buffer.from(signature, "hex"),
    Buffer.from(expected, "hex")
  );
}

// In your webhook handler:
app.post("/webhooks/getabrain", (req, res) => {
  const signature = req.headers["x-gab-signature"];
  const rawBody = req.rawBody; // Ensure you capture the raw body

  if (!verifyWebhookSignature(rawBody, signature, process.env.GAB_API_SECRET)) {
    return res.status(401).json({ error: "Invalid signature" });
  }

  const event = req.body;
  console.log("Received event:", event.event);

  // Process the event...

  res.status(200).json({ received: true });
});

Python Verification Example

import hmac
import hashlib
from flask import Flask, request, jsonify

app = Flask(__name__)

def verify_signature(payload, signature, secret):
    expected = hmac.new(
        secret.encode("utf-8"),
        payload,
        hashlib.sha256
    ).hexdigest()
    return hmac.compare_digest(expected, signature)

@app.route("/webhooks/getabrain", methods=["POST"])
def webhook_handler():
    signature = request.headers.get("X-GAB-Signature")
    if not verify_signature(request.data, signature, "your_api_secret"):
        return jsonify({"error": "Invalid signature"}), 401

    event = request.json
    print(f"Received event: {event['event']}")

    # Process the event...

    return jsonify({"received": True}), 200

Retry Policy

If your webhook endpoint returns a non-2xx status code or does not respond within 10 seconds, GetABrain.ai will retry the delivery with exponential backoff:

AttemptDelayDescription
1st retry1 minuteFirst retry after initial failure
2nd retry5 minutesSecond retry with increased delay
3rd retry30 minutesFinal retry attempt

After 3 failed retries, the webhook delivery is marked as failed. You can check for missed events by polling the query endpoint or reviewing your webhook delivery logs in the dashboard.

Best Practices

  • Always verify signatures -- never process a webhook without validating the X-GAB-Signature header.
  • Respond quickly -- return a 200 status code within 10 seconds. Perform heavy processing asynchronously.
  • Handle duplicates -- webhook deliveries may be retried, so use the event timestamp and query/response IDs to deduplicate.
  • Use HTTPS -- webhook URLs must use HTTPS. HTTP endpoints will be rejected.
  • Log everything -- store raw webhook payloads for debugging and auditing purposes.