Webhooks
Webhooks let Semantico notify your systems the moment a product generation task finishes — so you don’t have to poll. Semantico supports one webhook configuration per workspace.
Configure a webhook
Section titled “Configure a webhook”Manage your webhook through the settings endpoints:
GET /settings/webhooks/— retrieve the current configuration.POST /settings/webhooks/— create or replace the configuration (aPOSTalways replaces the single existing config).DELETE /settings/webhooks/— remove it.
Request body:
{ "url": "https://example.com/webhook", "hmac_secret": "your-shared-secret", "actions": ["product_generated", "product_generation_failed", "product_generation_no_urls"]}url(required) — where deliveries are sent.hmac_secret(optional) — if set, every delivery is signed so you can verify it came from Semantico.actions(optional) — which events to deliver. If omitted, all events are enabled.
Available events:
| Event | Fires when |
|---|---|
product_generated |
A product finished generating successfully. |
product_generation_failed |
Generation failed for a product. |
product_generation_no_urls |
No source URLs could be found for the product. |
Delivery behavior
Section titled “Delivery behavior”- Deliveries are
POSTrequests withContent-Type: application/json. - Timeout is 10 seconds.
- Failed deliveries are retried up to 5 times with backoff (roughly after 1s, 8s, 60s,
120s, and 420s). A
2xxis treated as success; a4xxis treated as permanent and is not retried; other responses (5xx, timeouts, connection errors) are retried. - Semantico won’t notify you twice for the same product-and-event pair.
Payload
Section titled “Payload”Each delivery contains:
action— the event name.status— the task status.reason_for_failure— an optional failure reason.credits— creditsconsumedandremaining.task— task metadata (id, timestamps, source URLs).product— the full product payload (ornullif unavailable). Custom attributes are returned under thesectionskey.
{ "action": "product_generated", "status": "SUCCESS", "reason_for_failure": "", "credits": { "consumed": 10, "remaining": 1230 }, "task": { "id": "b3b8c9c8-4a62-4b59-9caa-7d7b1f5a1234", "status": "SUCCESS", "created_at": "2026-02-09T10:00:00Z", "updated_at": "2026-02-09T10:01:00Z", "urls": [ { "id": 1, "url": "https://example.com/product", "status": "SUCCESS", "relevant_score": 0.95, "is_user_provided_link": true } ] }, "product": { "id": 123, "sku": "MODEL-001", "is_product_model": true }}Verifying the signature
Section titled “Verifying the signature”If you set an hmac_secret, each delivery includes two headers:
X-Webhook-Timestamp— Unix timestamp (seconds) of the delivery.X-Webhook-Signature—sha256=<hex>, an HMAC-SHA256 of the signed payload.
The signed payload is the timestamp and the raw request body joined by a dot:
signed_payload = "<X-Webhook-Timestamp>" + "." + "<raw JSON request body>"signature = "sha256=" + HMAC_SHA256(secret, signed_payload) // hex digestTo verify, recompute the signature over the raw body you received and compare it against
X-Webhook-Signature. If no hmac_secret is configured, these headers are not sent.
Verifying in Python:
import hashlibimport hmac
def is_valid(secret: str, timestamp: str, raw_body: bytes, signature: str) -> bool: signed = f"{timestamp}.".encode() + raw_body expected = "sha256=" + hmac.new(secret.encode(), signed, hashlib.sha256).hexdigest() return hmac.compare_digest(expected, signature)