Developer API
API reference

Webhooks

Instead of polling, let us push. Register a webhook and we POST the finished job to your endpoint the moment it completes — success or failure.

Registering a webhook

Register a webhook_url and webhook_secret with Antero Trail for your partner account, or pass a one-off callback_url on a specific POST /v1/conversions call.

On job completion we POST the exact same JSON body that GET /v1/conversions/{id} would return, with these headers:

Request headers
Content-Type: application/json
X-AIEA-Signature: sha256=<hex hmac>

Verify the signature

The signature is HMAC-SHA256(webhook_secret, raw_request_body), hex-encoded and prefixed sha256=. Verify it before trusting the payload, and always hash the raw received bytes — before any JSON re-serialization, since whitespace changes the hash.

Python

Python
import hashlib
import hmac

def verify_signature(raw_body: bytes, signature_header: str, secret: str) -> bool:
    """Verify X-AIEA-Signature: sha256=<hex>. raw_body must be the exact
    request bytes, before any JSON re-serialization (whitespace matters)."""
    if not signature_header.startswith("sha256="):
        return False
    expected = hmac.new(secret.encode("utf-8"), raw_body, hashlib.sha256).hexdigest()
    provided = signature_header.split("=", 1)[1]
    return hmac.compare_digest(expected, provided)

# Example (Flask-style):
# raw_body = request.get_data()  # bytes, NOT request.json
# ok = verify_signature(raw_body, request.headers["X-AIEA-Signature"], WEBHOOK_SECRET)

Node.js

JavaScript
const crypto = require("crypto");

function verifySignature(rawBody, signatureHeader, secret) {
  // rawBody must be a Buffer/string of the EXACT bytes received — capture it
  // before any JSON.parse (e.g. express.raw({ type: "application/json" })).
  if (!signatureHeader || !signatureHeader.startsWith("sha256=")) return false;
  const expected = crypto.createHmac("sha256", secret).update(rawBody).digest("hex");
  const provided = signatureHeader.split("=")[1];
  const a = Buffer.from(expected, "utf8");
  const b = Buffer.from(provided, "utf8");
  return a.length === b.length && crypto.timingSafeEqual(a, b);
}

// Example (Express):
// app.post("/webhook", express.raw({ type: "application/json" }), (req, res) => {
//   const ok = verifySignature(req.body, req.headers["x-aiea-signature"], WEBHOOK_SECRET);
//   if (!ok) return res.status(401).end();
//   const payload = JSON.parse(req.body);
// });

Delivery & retries

If your endpoint doesn't respond 2xx, we retry up to 3 times with backoff:

AttemptDelay after previous
1Immediate (on completion)
21 minute
35 minutes
425 minutes
Fallback

A webhook failure never changes the job's status. Polling GET /v1/conversions/{id} always works, regardless of whether your webhook receiver was up — treat the webhook as an optimization, and polling as the source of truth.