back to articles
Security
API Integration

Building Secure eKYC Workflows with ShuftiPro

Architectural patterns for integrating third-party identity verification APIs with robust error handling, retry logic, and comprehensive audit trails.

Feb 2026·11 min read

Electronic KYC (eKYC) is a non-negotiable compliance requirement in fintech. Integrating ShuftiPro requires more than a single API call — you need a hardened async pipeline that handles failures gracefully, maintains an immutable audit trail, and protects sensitive personal data.

Async Pipeline Architecture

  • ▸User submits documents → encrypted in S3, reference stored in DB
  • ▸Verification job dispatched to queue with exponential backoff
  • ▸ShuftiPro webhook updates verification status asynchronously
  • ▸Status change triggers downstream AML check
  • ▸All transitions logged to an append-only audit table

Secure Document Storage

Government-issued ID documents must never be stored unencrypted. Use AWS S3 with server-side encryption, private ACL, and obfuscated key paths:

php
public function storeDocument(UploadedFile $file, int $userId): string
{
    $key = sprintf(
        'kyc/%s/%s/%s',
        encrypt($userId),
        now()->format('Y/m'),
        Str::uuid() . '.' . $file->extension()
    );

    Storage::disk('s3-private')->put($key, $file->get(), [
        'ServerSideEncryption' => 'AES256',
        'ACL'                  => 'private',
    ]);

    return $key;
}

Webhook Signature Verification

Always verify the HMAC signature on incoming webhooks before processing. Spoofed callbacks are a real attack vector:

php
public function handleWebhook(Request $request): JsonResponse
{
    $expected = hash_hmac(
        'sha256',
        $request->getContent(),
        config('services.shufti.secret')
    );

    if (!hash_equals($expected, $request->header('Shufti-Signature', ''))) {
        Log::warning('Shufti webhook signature mismatch', ['ip' => $request->ip()]);
        return response()->json(['error' => 'Unauthorized'], 401);
    }

    VerificationStatusJob::dispatch($request->validated());
    return response()->json(['status' => 'accepted']);
}

Security Checklist

  • ▸Verify webhook HMAC signatures with constant-time comparison
  • ▸Store documents server-side encrypted with private ACL
  • ▸Never log PII — log reference IDs only
  • ▸Rate-limit document submission endpoints
  • ▸Use separate IAM roles for read vs write S3 access
  • ▸Auto-delete documents after the regulatory retention period