EVERYTHING Studios
API Documentation

Webhooks

Instead of polling, register an https URL and we push you an event every time one of your jobs changes state. Configure your endpoint below, verify every delivery's HMAC signature, and react to task.updated events.

Configuring your endpoint

Webhook configuration is per account: one endpoint URL and one signing secret. All five routes below are authenticated with your API key (either Authorization: Bearer esk_… or x-api-key: esk_…).

PUT/v1/webhook-endpoint

Register or update the URL that event deliveries are POSTed to.

FieldTypeRequiredDescription
urlstringyesThe https URL we POST events to. Non-https, missing, or otherwise invalid URLs are rejected with 400 invalid_request.

The first time you configure an endpoint, the response includes your signing secret: {"url":"…","secret":"…"}. Subsequent PUTs that change the URL return {"url":"…"} without the secret. The current secret is always retrievable with GET /v1/webhook-endpoint — rotate it with POST /v1/webhook-endpoint/rotate if it may have leaked.

curl -X PUT https://api.everythingstudios.ai/v1/webhook-endpoint \
  -H "Authorization: Bearer esk_YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"url":"https://your-app.example.com/webhooks"}'

GET/v1/webhook-endpoint

Retrieve your configured endpoint URL and signing secret.

Returns {"url":"…","secret":"…"}. If you have never configured an endpoint, returns 404 not_configured.

curl https://api.everythingstudios.ai/v1/webhook-endpoint \
  -H "Authorization: Bearer esk_YOUR_API_KEY"

DELETE/v1/webhook-endpoint

Remove your webhook endpoint. We stop delivering events immediately.

Returns 204 with an empty body.

curl -X DELETE https://api.everythingstudios.ai/v1/webhook-endpoint \
  -H "Authorization: Bearer esk_YOUR_API_KEY"

POST/v1/webhook-endpoint/rotate

Generate a new signing secret. The old secret stops working immediately.

Returns {"secret":"…"} with the new secret; the old secret stops working immediately. If no endpoint is configured, returns 404 not_configured.

curl -X POST https://api.everythingstudios.ai/v1/webhook-endpoint/rotate \
  -H "Authorization: Bearer esk_YOUR_API_KEY"

POST/v1/webhook-endpoint/test

Queue a signed ping event to your endpoint to verify your integration.

Returns 202 with {"queued":true}; a signed ping event arrives roughly 10 seconds later. If no endpoint is configured, returns 409 not_configured.

curl -X POST https://api.everythingstudios.ai/v1/webhook-endpoint/test \
  -H "Authorization: Bearer esk_YOUR_API_KEY"

Events

Every delivery is a JSON object with an event field telling you what happened. There are two event types.

task.updated

Sent whenever one of your jobs changes status or progress. The payload mirrors the task object from GET /v1/jobs/{id}.

FieldTypeRequiredDescription
eventstringalwaysThe literal string "task.updated".
event_idstringalwaysULID unique to this delivery.
user_idstringalwaysYour account id.
task_idstringalwaysULID of the job that changed — same value as job_id.
typestringalwaysAlways image-to-3d — the only supported job type.
statusstringalwaysOne of PENDING, QUEUED, IN_PROGRESS, SUCCEEDED, FAILED, CANCELED.
progressnumberalways0–100, advisory.
model_urlsobjectoptionalPresent when outputs are ready: contains the key glb — a permanent CDN URL. GLB is the only format produced today.
thumbnail_urlstringoptionalJPEG thumbnail on the same CDN.
errorobjectoptional{ code, message } — present on FAILED jobs.
timestampstringalwaysISO 8601 UTC.

ping

Sent by the test route to confirm your endpoint and signature check work. Handle it exactly like any other signed delivery.

FieldTypeRequiredDescription
eventstringalwaysThe literal string "ping".
event_idstringalwaysULID unique to this delivery.
user_idstringalwaysYour account id.
timestampstringalwaysISO 8601 UTC.

Verifying signatures

Every delivery carries two headers: x-generate-timestamp (ISO 8601, set at delivery time) and x-generate-signature. The signature is sha256= followed by the hex-encoded HMAC-SHA256 of your signing secret, computed over ${timestamp}.${rawBody} — the timestamp header, a literal dot, and the raw request body.

  • Verify against the raw request body, not a re-serialized parse of the JSON — key order or whitespace differences would change the bytes and break the signature.
  • Compare signatures in constant time (timingSafeEqual in Node, hmac.compare_digest in Python).
  • No replay window is enforced by the sender. You may add your own freshness check against x-generate-timestamp if your threat model calls for one.
Webhook signature verification
import crypto from "node:crypto";

// The secret returned when you first configured your endpoint
const WEBHOOK_SECRET = "YOUR_WEBHOOK_SECRET";

app.post(
  "/webhooks",
  express.raw({ type: "application/json" }), // keep the RAW body
  (req, res) => {
    const timestamp = req.get("x-generate-timestamp");
    const signature = req.get("x-generate-signature"); // "sha256=<hex>"

    const expected =
      "sha256=" +
      crypto
        .createHmac("sha256", WEBHOOK_SECRET)
        .update(timestamp + "." + req.body) // req.body is a Buffer
        .digest("hex");

    // Constant-time comparison
    const a = Buffer.from(signature);
    const b = Buffer.from(expected);
    if (a.length !== b.length || !crypto.timingSafeEqual(a, b)) {
      return res.status(400).send("Invalid signature");
    }

    const event = JSON.parse(req.body);
    if (event.event === "task.updated" && event.status === "SUCCEEDED") {
      // Model is ready — enqueue your own async work here
    }

    // Acknowledge immediately; the sender only waits 10 seconds
    res.status(202).end();
  }
);

Delivery behavior

  • Deliveries are HTTP POST with Content-Type: application/json to your configured URL.
  • The sender waits 10 seconds for a response; any 2xx status counts as success.
  • Failed deliveries are retried up to 5 times, then dropped and dead-lettered for 14 days.
  • Return a 2xx as fast as possible and do slow work asynchronously — storage, notifications, downstream processing — so deliveries never time out.