Skip to main content
Subscribe to webhooks instead of polling. Configure a webhook endpoint and subscribe to the job types you care about. You’ll receive a POST when the job completes or fails.

Event Payload

The webhook payload wraps the job result:
{
  "id": "evt_01JABCD999",
  "type": "parse.completed",
  "timestamp": "2024-01-15T10:01:30Z",
  "data": {
    "job_id": "job_01JABCD123",
    "type": "parse",
    "status": "completed",
    "target": {
      "type": "sheet",
      "id": "sht_01JABCD100"
    },
    "results": {
      "legend": [
        {
          "block_id": "blk_01JABCD200",
          "symbols_detected": 24,
          "symbols_with_graphic": 18,
          "symbols": [
            {
              "feature_id": "ftr_01JABCD300",
              "label": "duplex_receptacle",
              "description": "DUPLEX RECEPTACLE",
              "has_graphic": true
            }
          ]
        }
      ],
      "symbols": [
        {
          "label": "duplex_receptacle",
          "description": "DUPLEX RECEPTACLE",
          "legend_feature_id": "ftr_01JABCD300",
          "instance_count": 4,
          "instances": [
            {
              "feature_id": "ftr_01JABCD400",
              "block_id": "blk_01JABCD100",
              "bounds": { "x_min": 333, "y_min": 207, "x_max": 345, "y_max": 222 },
              "confidence": 0.694
            }
          ]
        }
      ],
      "summary": {
        "total_symbols_searched": 5,
        "total_instances_found": 21
      }
    },
    "completed_at": "2024-01-15T10:01:30Z"
  }
}
When a job fails:
{
  "id": "evt_01JABCD998",
  "type": "parse.failed",
  "timestamp": "2024-01-15T10:01:30Z",
  "data": {
    "job_id": "job_01JABCD123",
    "type": "parse",
    "status": "failed",
    "target": {
      "type": "sheet",
      "id": "sht_01JABCD100"
    },
    "error": {
      "code": "PROCESSING_ERROR",
      "message": "Unable to process sheet - source image missing"
    },
    "failed_at": "2024-01-15T10:01:30Z"
  }
}

Event Types

Event types follow the pattern {job_type}.{status}. This allows subscribing to all status changes for a job type with a single subscription prefix.
EventDescription
parse.queuedParse job has been queued
parse.startedParse job has started processing
parse.completedParse job completed successfully
parse.failedParse job failed

Incremental Parse Events

Parse fires parse.block.completed events as each block finishes symbol detection, so you can process results incrementally rather than waiting for the entire job:
{
  "id": "evt_01JABCD997",
  "type": "parse.block.completed",
  "timestamp": "2024-01-15T10:01:15Z",
  "data": {
    "job_id": "job_01JABCD124",
    "parse_job_id": "job_01JABCD123",
    "block_id": "blk_01JABCD100",
    "type": "parse.block",
    "status": "completed",
    "results": {
      "symbols": [
        {
          "label": "duplex_receptacle",
          "description": "DUPLEX RECEPTACLE",
          "instance_count": 4,
          "instances": [
            {
              "feature_id": "ftr_01JABCD400",
              "bounds": { "x_min": 333, "y_min": 207, "x_max": 345, "y_max": 222 },
              "confidence": 0.694
            }
          ]
        }
      ],
      "summary": {
        "total_symbols_searched": 5,
        "total_instances_found": 21
      }
    },
    "completed_at": "2024-01-15T10:01:15Z"
  }
}
The incremental event includes per-block results without the legend field (that appears only in the final parse.completed event).

Child Job Events

Long-running parse jobs create internal child jobs (legend parsing, symbol matching). You can subscribe to parse.child.{status} events for granular progress tracking:
EventDescription
parse.child.queuedChild job queued
parse.child.startedChild job started
parse.child.completedChild job completed
parse.child.failedChild job failed
Child events include parent_job_id and child_job_type fields for correlation.

Subscriptions

When configuring a webhook, you subscribe to specific event prefixes. Only events matching your subscribed prefixes are delivered.

Verifying Signatures

Every webhook request includes an HMAC-SHA256 signature for verification:
HeaderDescription
X-Webhook-IdUnique delivery ID
X-Webhook-TimestampUnix timestamp of the request
X-Webhook-Signaturev1=<hmac-sha256> signature
The signature is computed over {timestamp}.{payload} using your webhook secret:
const crypto = require("crypto");

function verify(payload, headers, secret) {
  const timestamp = headers["x-webhook-timestamp"];
  const expected = headers["x-webhook-signature"].replace("v1=", "");
  const signature = crypto
    .createHmac("sha256", secret)
    .update(`${timestamp}.${payload}`)
    .digest("hex");
  return crypto.timingSafeEqual(
    Buffer.from(signature),
    Buffer.from(expected)
  );
}

Retry Behavior

Failed deliveries are retried with exponential backoff up to 5 attempts. After 5 consecutive failures, the webhook endpoint is disabled (circuit breaker). Re-enable it from the Developer settings.