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:
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
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
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:
| Attempt | Delay after previous |
|---|---|
| 1 | Immediate (on completion) |
| 2 | 1 minute |
| 3 | 5 minutes |
| 4 | 25 minutes |
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.