RealDoc Async Task Webhook User Guide

Feature Description

The RealDoc Webhook is used to notify you of the final status of asynchronous document processing tasks. After you submit a task via the Asynchronous Document Processing API, ZOLOZ sends an HTTP POST request to your configured callback URL when the task enters the Success or Failure state.

The Webhook notifies you of the task status only and does not include the full processing results. Upon receiving the notification, please use the transactionId to call the async task status query or result query API to retrieve the detailed results.

Activation Instructions

The Webhook is activated on a per-merchant and per-environment basis. To enable it, please contact your ZOLOZ Customer Manager or Technical Support, and provide the target environment and merchant information.

Usage Workflow

Prerequisites

  • Activate Webhook feature and Portal permissions: Please contact ZOLOZ to enable them.
    • After Portal permissions are granted, please log out and log back in to take effect.
    • The Webhook management feature is currently available to Administrator, Operator, and custom roles with the required permissions.
  • Prepare a callback URL: Prepare a public HTTPS URL to receive callbacks.

Configuration Steps

  1. Log in to the ZOLOZ Portal.
  2. In the left navigation pane, choose Integration > Webhook to go to the Webhook Configuration page.
  3. Create a Webhook and select RealDoc Async as the event type.
  4. Fill in the configuration details, including the HTTPS callback URL, Secret, digest algorithm, etc. After saving, perform a test to verify connectivity.
  5. After the test succeeds, enable the configuration.
  6. Once you submit a task via the RealDoc Async API, ZOLOZ will send a Webhook notification when the task completes. After receiving the notification, use the transactionId to query the complete results.

Callback Protocol

Request Overview

Item

Description

Method

POST

Content-Type

application/json

Callback URL

The public HTTPS address configured in the Portal

Trigger Timing

When an async task enters Success or Failure state

Request Headers

HTTP header names are case-insensitive.

Header

Description

X-Payload-Digest

The HMAC signature (hex string) of the raw request body computed using the Secret configured in the Portal.

X-Payload-Digest-Alg

The signature algorithm. Supported values: HMAC_SHA256_HEX or HMAC_SHA512_HEX.

Request Body

Example:

copy
{
    "transactionId": "R000000202607211713541766****", 
    "status": "Success", 
    "productType": "REALDOC_DOCUMENT_EXTRACTION"
}

Field Descriptions:

Field

Description

transactionId

The unique identifier of the asynchronous task. This is the same as the task ID returned in the async upload API response. Used for idempotent processing and result query.

status

The final status of the task. Either Success or Failure.

productType

The RealDoc product type. Supported values:

  • REALDOC_FORGERY_DETECTION
  • REALDOC_DOCUMENT_EXTRACTION
  • REALDOC_DOCUMENT_INSIGHT
  • REALDOC_CROSS_MATCHING
  • REALDOC_DOCUMENT_PARSING

Java Code Sample for Receiving and Verifying Signatures

This sample is applicable to Java 8 and Spring Boot. Important notes for signature verification:

  • Algorithm support: Verification supports both HMAC_SHA256_HEX and HMAC_SHA512_HEX signature algorithms.
  • Critical requirement: Signature verification must be performed on the raw request body as received via HTTP. Do not format, deserialize, or re-serialize the body, as doing so will cause signature mismatches.
copy
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.util.EnumMap;
import java.util.Map;

import org.apache.commons.codec.digest.HmacAlgorithms;
import org.apache.commons.codec.digest.HmacUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/webhooks")
public class RealDocWebhookController {

    private static final Logger log = LoggerFactory.getLogger(RealDocWebhookController.class);

    private static final String HEADER_DIGEST     = "X-Payload-Digest";
    private static final String HEADER_DIGEST_ALG = "X-Payload-Digest-Alg";

    private static final String ALG_HMAC_SHA256_HEX = "HMAC_SHA256_HEX";
    private static final String ALG_HMAC_SHA512_HEX = "HMAC_SHA512_HEX";

    private final Map<HmacAlgorithms, HmacUtils> hmacUtilsMap = new EnumMap<>(HmacAlgorithms.class);

    public RealDocWebhookController(@Value("${realdoc.webhook.secret:}") String secret) {
        hmacUtilsMap.put(HmacAlgorithms.HMAC_SHA_256, new HmacUtils(HmacAlgorithms.HMAC_SHA_256, secret));
        hmacUtilsMap.put(HmacAlgorithms.HMAC_SHA_512, new HmacUtils(HmacAlgorithms.HMAC_SHA_512, secret));
    }

    @PostMapping(value = "/realdoc", consumes = MediaType.APPLICATION_JSON_VALUE)
    public ResponseEntity<Void> receive(@RequestBody String rawBody, @RequestHeader(HEADER_DIGEST) String digest,
            @RequestHeader(HEADER_DIGEST_ALG) String algorithm) {

        HmacAlgorithms hmacAlg = resolveAlgorithm(algorithm);
        if (hmacAlg == null) {
            log.warn("Unsupported digest algorithm: {}", algorithm);
            return ResponseEntity.badRequest().build();
        }

        String expected = hmacUtilsMap.get(hmacAlg).hmacHex(rawBody);
        boolean valid = MessageDigest.isEqual(expected.getBytes(StandardCharsets.UTF_8), digest.getBytes(StandardCharsets.UTF_8));
        if (!valid) {
            log.warn("Webhook signature verification failed, algorithm={}", algorithm);
            return ResponseEntity.status(HttpStatus.UNAUTHORIZED).build();
        }

        // Process the event idempotently by transactionId.

        return ResponseEntity.ok().build();
    }

    private HmacAlgorithms resolveAlgorithm(String algorithm) {
        switch (algorithm) {
            case ALG_HMAC_SHA256_HEX:
                return HmacAlgorithms.HMAC_SHA_256;
            case ALG_HMAC_SHA512_HEX:
                return HmacAlgorithms.HMAC_SHA_512;
            default:
                return null;
        }
    }
}

Important Notes

  • Upon successful receipt, please return an HTTP 2xx status code to ZOLOZ as soon as possible. If your business processing takes a significant amount of time, we recommend delegating time-consuming operations to an internal queue for asynchronous processing.
  • If ZOLOZ does not receive a 2xx response, or if a timeout or network error occurs, retries may be triggered, and the same notification may be delivered multiple times. Please use the transactionId to ensure idempotent processing.
  • Webhook delivery failures do not affect the results of RealDoc tasks. You can still call the status query and result query APIs periodically to retrieve the final task results.
  • Webhooks are triggered only by tasks submitted through the Async API. Tasks initiated from the Portal Experience Center do not trigger Webhook notifications.
  • If the Webhook is not activated or configured, the original Async APIs remain fully functional. You can still poll the status via the status query and result query APIs.
  • The Secret should be stored securely in both the Portal and your key management system. Do not log it or commit it to your code repository.